Gym102832K. Ragdoll(CCPC长春)

题意:

n个点,每个点都有自己的权值aia_iai​,一开始第i个点在第i个集合里。
如果一对点(i,j)是bad当且仅当满足i和j在同一个集合里,且gcd(ai,aj)=ai⊕ajgcd(a_i,a_j)=a_i⊕a_jgcd(ai​,aj​)=ai​⊕aj​
现在有3种操作:

  1. 新增一个点x(在新的一个集合里),值为v
  2. 合并两个集合x和y
  3. 将点x的值改为v

有m个操作,请输出每次操作后有多少对点是bad

题解:

一开始无从下手,因为这个式子不知道该如何应用
这里有个很巧妙的转换,ai⊕aj​⩾∣ai−aj∣​⩾gcd(ai,aj)a_i⊕a_j ​⩾|a_i-a_j|​⩾gcd(a_i,a_j)ai​⊕aj​​⩾∣ai​−aj​∣​⩾gcd(ai​,aj​),如果我们要让等式成立,则需要满足:

  1. ai​⊕aj=∣ai−aj∣a_i​⊕a_j=|a_i-a_j|ai​​⊕aj​=∣ai​−aj​∣
  2. ∣ai−aj∣=gcd(ai,aj)|a_i-a_j|=gcd(a_i,a_j)∣ai​−aj​∣=gcd(ai​,aj​)

转换证明:
a⊕b>=a−ba⊕b>=a-ba⊕b>=a−b,这个式子是显然成立的,用二进制思想去考虑
gcd(a,b)<=a−bgcd(a,b)<=a-bgcd(a,b)<=a−b:由于gcd(a,b)是b的因子,gcd(a,b)是a的因子,所以gcd(a,b)就是(a-b)的因子,所以gcd(a,b)一定小于等于a-b

∣ai−aj∣=gcd(ai,aj)|a_i-a_j|=gcd(a_i,a_j)∣ai​−aj​∣=gcd(ai​,aj​)
aj=ai+gcd(ai,aj)a_j=a_i+gcd(a_i,a_j)aj​=ai​+gcd(ai​,aj​)或者aj=ai−gcd(ai,aj)a_j=a_i-gcd(a_i,a_j)aj​=ai​−gcd(ai​,aj​)
而gcd(ai,aj)gcd(a_i,a_j)gcd(ai​,aj​)是aia_iai​的因子
因此我们可以枚举aia_iai​,然后枚举aia_iai​的因子,这样就求出对于aia_iai​而言合法的aja_jaj​,用vector存下来。相当于我们可以预处理出所有是bad的点对
我们再看三个操作,这种集合操作,都可以用并查集来维护
对于操作1,就是新加一个集合
对于操作2,合并两个集合,如果我们直接盲目合并,复杂度是O(n),这里用到启发式合并,把小的集合合并到大的集合中,这样复杂度降低为O(log)
对于操作3,我们先去掉原先元素可能组成的bad点对,改成数v后再加入新的bad点对
在每次操作中,维护着答案,如何维护呢?因为我们预处理出所有点对,合并时两个集合时,就枚举小集合所有元素,看大集合内有多少可以构成bad点对,更新答案ans。修改数时就是删除原先,加上新填
unordered_map速度更快
详细内容看代码

代码:

#include <bits/stdc++.h>
#include <unordered_map>
#define debug(a, b) printf("%s = %d\n", a, b);
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> PII;
clock_t startTime, endTime;
//Fe~Jozky
const ll INF_ll= 1e18;
const int INF_int= 0x3f3f3f3f;
void read(){};
template <typename _Tp, typename... _Tps> void read(_Tp& x, _Tps&... Ar)
{x= 0;char c= getchar();bool flag= 0;while (c < '0' || c > '9')flag|= (c == '-'), c= getchar();while (c >= '0' && c <= '9')x= (x << 3) + (x << 1) + (c ^ 48), c= getchar();if (flag)x= -x;read(Ar...);
}
template <typename T> inline void write(T x)
{if (x < 0) {x= ~(x - 1);putchar('-');}if (x > 9)write(x / 10);putchar(x % 10 + '0');
}
void rd_test()
{#ifdef ONLINE_JUDGE
#elsestartTime = clock ();freopen("data.in", "r", stdin);
#endif
}
void Time_test()
{#ifdef ONLINE_JUDGE
#elseendTime= clock();printf("\nRun Time:%lfs\n", (double)(endTime - startTime) / CLOCKS_PER_SEC);
#endif
}
const int maxn=5e5+9;
int n,m;
int a[maxn];
vector<int>g[maxn];
int fa[maxn];
int sz[maxn];
ll ans=0;
unordered_map<int,ll>mp[maxn];
int gcd(int a,int b){if(b)return gcd(b,a%b);return a;
}
int find(int x){if(fa[x]==x)return x;return fa[x]=find(fa[x]);
}
void work(int x){for(int i=1;i*i<=x;i++){//枚举因子 if(x%i==0){int tmp=x-i;if((tmp^x)==gcd(tmp,x)&&tmp!=0)//符合bad的点对 g[x].push_back(tmp);tmp=x+i; if((tmp^x)==gcd(tmp,x)&&tmp<=200000)g[x].push_back(tmp);if(i!=x/i){tmp=x-x/i;if((tmp^x)==gcd(tmp,x)&&tmp!=0)g[x].push_back(tmp);tmp=x+x/i;if((tmp^x)==gcd(tmp,x)&&tmp!=200000)g[x].push_back(tmp);} }}
}
void merge(int x,int y){int fx=find(x);int fy=find(y);if(fx==fy)//如果在一个集合退出 return ;//我们让fy是小集合,用fy去合并到fx if(sz[fx]<sz[fy]) swap(fx,fy);//合并时更新答案ans for(auto& it:mp[fy]){//遍历小集合内的所有元素it.first
//      cout<<"it.first="<<it.first<<endl;for(int i=0;i<g[it.first].size();i++){//          printf("g[it.first].size()=%d\n",g[it.first].size());
//          printf("g[it.first][%d]=%d\n",i,g[it.first][i]);//看大集合中有多少点可以与小集合的元素组成bad点对 if(mp[fx].count(g[it.first][i]))ans+= 1ll * it.second * mp[fx][g[it.first][i]];}}for(auto &it:mp[fy]){//把fy的所有元素存放到集合fx中,即合并 mp[fx][it.first]+=it.second;} mp[fy].clear();sz[fx]+=sz[fy];fa[fy]=fx;
}
int main()
{rd_test();for(int i=1;i<=200002;i++){work(i);//预处理出所有bad点对 }cin>>n>>m;for(int i=1;i<=n+m;i++){fa[i]=i; sz[i]=1;//集合大小}for(int i=1;i<=n;i++){cin>>a[i];mp[i][a[i]]++;//每个集合元素以及大小 }for(int i=1;i<=m;i++){//添加 int op,x,y;cin>>op>>x>>y;if(op==1){a[x]=y;sz[x]=1;mp[x][y]=1;}else if(op==2){//合并操作 merge(x,y);}else if(op==3){//修改值 int u=find(x);for(int i=0;i<g[a[x]].size();i++){//删除原集合内的bad点对 if(mp[u].count(g[a[x]][i]))ans-=mp[u][g[a[x]][i]]; }mp[u][a[x]]--;//点a[x]删除a[x]=y;//修改新值for(int i=0;i<g[a[x]].size();i++){if(mp[u].count(g[a[x]][i]))ans+=mp[u][g[a[x]][i]]; }mp[u][a[x]]++;//加上新点 }printf("%lld\n",ans); }return 0;//Time_test();
}

Gym102832K. Ragdoll(CCPC长春)相关推荐

  1. ccpc长春-K. Ragdoll(启发式合并、并查集、gcd转化)map的神奇tle

    K. Ragdoll 菜死我得了 题意:给定一组点以及他们的权重(1⩽n⩽1051\leqslant n\leqslant10^51⩽n⩽105),起初所有点都是独立的点.给定三种操作: 1xv1\ ...

  2. 2020 CCPC 长春 K. Ragdoll(预处理+启发式合并)

    LINK 考虑x⊕y=gcd(x,y)x\oplus y=gcd(x,y)x⊕y=gcd(x,y)的情况应该是非常少的 不妨设x>yx>yx>y 由于x⊕y>=x−y>= ...

  3. L. Coordinate Paper(CCPC 长春)构造

    L. Coordinate Paper 构造一个长度为nnn的序列aaa,满足ai≥0a_i \geq 0ai​≥0,∑i=1nai=s\sum\limits_{i = 1} ^{n} a_i = s ...

  4. 2016年CCPC/ICPC比赛总结

    2016:我大一升入大二. 队友:GLY,WZK. 队名:开心就好(MyHappinessIsAll) 菜鸡战绩:5月省赛二等,9月东北四省赛二等,9月CCPC长春打铁,10月ICPC沈阳压线铜牌. ...

  5. ACM里的生成函数初探

    生成函数 前情提要:由于本人不太喜欢写博客(懒),所以写的时候一般是由于某些原因写的专题内容.这次是2020CCPC长春热身赛的D题没做出来. 大佬论文放前面:推荐阅读:毛杰明 母函数的性质及应用 基 ...

  6. XCPC2020赛季流水账

    前言 由于疫情原因,2020赛季最后一场比赛沈阳站在2021年7月18日才正式比完=.=,我的2020赛季也随之结束了. 回望这个赛季,感觉自己还是有点菜,拿奖全靠队友带QAQ,总共拿了: CCPC长 ...

  7. 2017.12退役贴

    本来青岛赛区打完准备写退役贴的,结果刚写完教练就叫我们准备哈尔滨和上海的final,结果就赶紧把发出来的退役贴给删了,现在打完了最后一场比赛准备发出来,然后发现找不到了,那既然这样就重开一贴吧. 先讲 ...

  8. 2020CCPC长春 K Ragdoll

    题目链接 Once there was a lovely ragdoll cat, named Little Zara, who liked trees and math. One day she m ...

  9. 2020CCPC(长春) - Ragdoll(启发式合并+带权并查集)

    题目大意:初始时给出 n 个集合,每个集合中都包含有一个数字,现在要求执行 m 次操作,每次操作分为下列三种类型: 1 x y:在 x 位置新建一个集合,并且放置一个数字 y 2 x y:合并集合 x ...

最新文章

  1. SparkSQL和Hadoop(面向数据科学家和大数据分析师)
  2. 终于有人把云计算、物联网和大数据讲明白了
  3. c# webapi POST 参数解决方法
  4. OpenSSL常用命令快速上手
  5. 借教室(NOIP2012)
  6. mysql5.5表的创建源码_mysql5.5 源码安装
  7. 实战:通过组策略为用户部署软件
  8. strip(),replace()和re.sub()用法
  9. OpenShift 4 - DevSecOps Workshop (1) - 安装 Workshop 环境
  10. selenium-03-常用操作
  11. SQL--JOIN之完全用法
  12. PHP面向对象深入研究之【对象生成】
  13. Java二分查找代码
  14. 数据库系统概念读书笔记-SQL标准简介
  15. php如何让图片自适应屏幕,css如何让图片自适应屏幕大小
  16. 根据轨道根数解算位置速度
  17. Decorate 模式
  18. RFID亮灯电子标签在仓储管理中的应用
  19. 用微PE工具箱安装系统
  20. 计算机网络 之 DNS (Domain Name System)域名服务器

热门文章

  1. 各个大学的录取通知书,哪个颜值最高?
  2. java aop execution_Spring AOP -- execution表达式
  3. c++ 异步下获取线程执行结果_前端异步编程的那些事
  4. python3多线程queue_Python多线程(3)——Queue模块
  5. java web请求字符串处理_java web工作常用技能篇(三)-封装复杂请求对象2
  6. java执行更新sql_sql server执行更新需要更多时间
  7. r语言用行名称提取数据框信息显示na_学会这些R语言技巧至少可以节省半年时间...
  8. web高德地图怎么加载离线地图_怎么验证全国离线卫星地图缓存文件的完整性
  9. java面向对象使用字符串_java面向对象中的String类中12种常用的方法
  10. vue 调用mutation方法_Vuex白话教程第三讲:Vuex旗下的Mutation