UVA 11987 几乎就是并查集= =
2016-02-10 02:04
399 查看
题目点这
题意,给很多个数,每个数刚开始自己是一个集合
要求满足3中操作:
1:将i与j两个数所在的集合合并
2:将i从原来集合中移除,扔进j所在集合中
3:询问i所在的集合有多少个数字,和是多少
1,3相当简单,赤裸裸的并查集就好了
麻烦的是2,并查集是单向的,只知道父亲不知道儿子
所有如果i是叶子节点无所谓
如果是父亲节点就会十分的蛋疼:
你是到另一个集合里面去了,你的儿子们呢?
把i这个点复制一下(编号为cnt++),当作叶子节点扔到新集合里去
原来的i仅仅有作为集合编号的作用,可以认为是个空点
:)实际上这个是剪切辣
一个小想法:并查集本质可以理解成树,
可以考虑就直接用链表写,记录每个节点的儿子们,
要是父亲要走了,把儿子们接到父亲的父亲上去,
如果这个父亲是根,随便选一个儿子当父亲(好丧失- -)
似乎十分暴力,感觉可行(如果没有极端数据的话)
找个时间试试这个想法,要是TLE了,,,当我没说
代码
运行结果
果然T了,
果然是不知是我写的常数大了还是数据是防暴力的
(((前面三次WA是因为自己手残
T_T不要在意这些细节。。。
题意,给很多个数,每个数刚开始自己是一个集合
要求满足3中操作:
1:将i与j两个数所在的集合合并
2:将i从原来集合中移除,扔进j所在集合中
3:询问i所在的集合有多少个数字,和是多少
1,3相当简单,赤裸裸的并查集就好了
麻烦的是2,并查集是单向的,只知道父亲不知道儿子
所有如果i是叶子节点无所谓
如果是父亲节点就会十分的蛋疼:
你是到另一个集合里面去了,你的儿子们呢?
把i这个点复制一下(编号为cnt++),当作叶子节点扔到新集合里去
原来的i仅仅有作为集合编号的作用,可以认为是个空点
:)实际上这个是剪切辣
[code]#include<cstdio> #include<cstring> #include<iostream> using namespace std; const int N=200000; int n,m,ask,i,j; int home ,id ,num ,sum ; bool root ; int find(int x){ if (home[x]==x)return x; home[x]=find(home[x]); return home[x]; } int main(){ //freopen("fuck.in","r",stdin); for (;scanf("%d%d",&n,&m)==2;){ int cnt=n; memset(root,0,sizeof(root)); for (i=1;i<=n;i++){ home[i]=i; id[i]=i; sum[i]=i; num[i]=1; } for (;m--;){ scanf("%d%d",&ask,&i); if (ask==1){ scanf("%d",&j); if (id[i]!=i) i=id[i]; if (id[j]!=j) j=id[j]; if (find(i)==find(j))continue; sum[find(j)]+=sum[find(i)]; num[find(j)]+=num[find(i)]; home[find(i)]=find(j); root[find(j)]=1; } if (ask==2){ scanf("%d",&j); int w=i; if (id[i]!=i) i=id[i]; if (id[j]!=j) j=id[j]; num[find(i)]--; num[find(j)]++; sum[find(i)]-=w; sum[find(j)]+=w; if (root[i]) {cnt++;id[i]=cnt;i=cnt;} home[i]=find(j); root[find(j)]=1; } if (ask==3){ if (id[i]!=i) i=id[i]; j=find(i); printf("%d %d\n",num[j],sum[j]); } } } return 0; }
一个小想法:并查集本质可以理解成树,
可以考虑就直接用链表写,记录每个节点的儿子们,
要是父亲要走了,把儿子们接到父亲的父亲上去,
如果这个父亲是根,随便选一个儿子当父亲(好丧失- -)
似乎十分暴力,感觉可行(如果没有极端数据的话)
找个时间试试这个想法,要是TLE了,,,当我没说
代码
[code]#include<cstdio> #include<cstring> #include<iostream> using namespace std; const int N=200000; int n,m,ask,i,j,k,root; int head ,next ; int fa ,sum ,num ; int main(){ //freopen("fuck.in","r",stdin); for (;scanf("%d%d",&n,&m)==2;){ for (i=1;i<=n;i++){ num[i]=1; sum[i]=i; } memset(fa,0,sizeof(fa));//Father for (;m--;){ scanf("%d%d",&ask,&i); if (ask==1){ scanf("%d",&j); while(fa[i])i=fa[i]; while(fa[j])j=fa[j]; next[i]=head[j]; head[j]=i; sum[j]+=sum[i]; num[j]+=num[i]; } if (ask==2){ scanf("%d",&j); if (fa[i]==0){ int root=k=head[i]; sum[k]=sum[i]-i; sum[i]=i; sum[j]+=i; num[k]=num[i]-1; num[i]=1; num[j]++; fa[k]=0; fa[i]=j; next[i]=head[j]; head[j]=i; for (k=next[k];k;k=next[k]){ fa[k]=root; next[k]=head[root]; head[root]=k; } } else if(head[i]){ int root=i; while (fa[root])root=fa[root]; num[root]--; num[j]++; sum[root]-=i;sum[j]+=i; for (k=head[i];k;k=next[k]) { fa[k]=root; next[k]=head[root]; head[root]=k; } } else { num[j]++;sum[j]+=i; fa[i]=j; next[i]=head[j]; head[j]=i; } } if (ask==3){ while(fa[i])i=fa[i]; printf("%d\n",sum[i]); } } } return 0; }
运行结果
果然T了,
果然是不知是我写的常数大了还是数据是防暴力的
(((前面三次WA是因为自己手残
T_T不要在意这些细节。。。
相关文章推荐
- PHP 字符串
- PHP 数组
- 算法导论例程——哈希表
- 【NYOJ】[599]奋斗的小蜗牛
- sqlserver中判断表或临时表是否存在
- UVA 10617 Again Palindrome
- bzoj1965: [Ahoi2005]SHUFFLE 洗牌
- MySQL时间戳转换
- 【Linux命令】grep命令
- 36. Valid Sudoku
- HDU 4508湫湫系列故事――减肥记i-简单的完全背包
- 【Linux命令】crontab命令
- python学习笔记——文件操作
- 【NYOJ】[477]A+B Problem III
- 1012. The Best Rank (25)
- 290. Word Pattern
- HDU 4563 御剑术I(背包)
- HDU 4501 小明系列之买年货(三维背包)
- 【NYOJ】[811]变态最大值
- 1. Two Sum