hihocoder1629:Graph (分块+并查集)
来源:互联网 发布:姚明41分比赛数据 编辑:程序博客网 时间:2024/06/07 07:31
题目传送门:http://hihocoder.com/problemset/problem/1629
题目大意:给出一幅n个点,m条边的无向图,然后给出q组询问。每组询问给定一个区间[L,R],问[L,R]中有多少点对可以相互到达。可以到达的要求是只能走[L,R]中的点。不超过5组数据,n,m<=50000,q<=100000。
题目分析:这题应该算是bzoj4537的弱化版吧。
所谓分块,就是在暴力的基础上优化,所以我们先考虑怎么暴力。可以枚举一个左端点L,然后将右端点R从左往右扫,并用一个并查集维护答案。合并两个大小分别为x和y的集合时,对答案的新贡献就是x*y。然后离线回答[L,R]的询问。
上述方法的缺点就是要枚举的L太多,时间是
当我们枚举第i个关键点为左端点的时候,同时处理所有左端点在(第i-1个关键点,第i个关键点],右端点在第i个关键点及之后的询问,将这些询问按右端点从小到大排序。假设当前的询问区间是[L,R],则我们先将[第i个关键点,R]的边加进并查集,这个部分可以用启发式合并+路径压缩。然后将[L,第i个关键点)的边用启发式合并加进并查集(不能用路径压缩,因为要撤销),并用一个栈记录操作。处理完询问后将栈里的操作还原。
如果某个询问没有跨越任何一个关键点,就说明区间内的边数小于
那么这样时间复杂度是多少呢?由于启发式合并+路径压缩的并查集,运行时间非常小,可以认为接近均摊
但其实如果将块的大小控制在
CODE:
#include<iostream>#include<string>#include<cstring>#include<cmath>#include<cstdio>#include<cstdlib>#include<stdio.h>#include<algorithm>using namespace std;const int maxn=50100;struct edge{ int obj; edge *Next;} e[maxn<<1];edge *head[maxn];int cur;int num[maxn];int a[maxn];int Prev[maxn];int cnt1;int fst[maxn];int id[maxn];int cnt2;struct data{ int L,R,Time;} ask[maxn<<1];int ans[maxn<<1];int fa[maxn];int Size[maxn];int tp;int fa1[maxn];int Size1[maxn];int tp1=0;int sak[maxn];int tail;int t,n,m,q;int step;void Add(int x,int y){ cur++; e[cur].obj=y; e[cur].Next=head[x]; head[x]=e+cur;}bool Comp1(data x,data y){ return x.L<y.L;}bool Comp2(data x,data y){ return x.R<y.R;}int Up(int x){ if (x==fa[x]) return x; return (fa[x]=Up(fa[x]));}void Merge(int x,int y){ x=Up(x); y=Up(y); if (x==y) return; if (Size[x]<Size[y]) swap(x,y); fa[y]=x; tp+=( Size[x]*Size[y] ); Size[x]+=Size[y];}int Find(int x){ if (x==fa[x]) return x; return Find(fa[x]);}void Push(int x,int y){ x=Find(x); y=Find(y); if (x==y) return; if (Size[x]<Size[y]) swap(x,y); fa[y]=x; tp+=( Size[x]*Size[y] ); Size[x]+=Size[y]; sak[++tail]=y;}void Clear(){ while (tail) { int x=sak[tail]; Size[ fa[x] ]-=Size[x]; tp-=( Size[x]*Size[ fa[x] ] ); fa[x]=x; tail--; }}int Up1(int x){ if (x==fa1[x]) return x; return (fa1[x]=Up1(fa1[x]));//一开始这里的Up1打成了Up!!!}void Merge1(int x,int y){ x=Up1(x); y=Up1(y); if (x==y) return; if (Size1[x]<Size1[y]) swap(x,y); fa1[y]=x; tp1+=( Size1[x]*Size1[y] ); Size1[x]+=Size1[y];}int main(){ freopen("1629.in","r",stdin); freopen("1629.out","w",stdout); scanf("%d",&t); while (t--) { scanf("%d%d%d",&n,&m,&q); cur=-1; for (int i=1; i<=n; i++) head[i]=NULL,num[i]=id[i]=Prev[i]=0; for (int i=1; i<=m; i++) { int x,y; scanf("%d%d",&x,&y); Add(x,y); Add(y,x); num[x]++; num[y]++; } cnt1=0; for (int i=1; i<=n; i++) if (num[i]) a[++cnt1]=i,Prev[i]=i; for (int i=1; i<=n; i++) if (!Prev[i]) Prev[i]=Prev[i-1]; cnt2=0; step=(int)floor( sqrt( (double)(2*m)*(double)(log(n)/0.3) )+1e-5 ); int now=0; for (int i=1; i<=n; i++) { now+=num[i]; if (now>=step) { fst[++cnt2]=i; id[i]=cnt2; now=0; } } if (fst[cnt2]<n) fst[++cnt2]=n,id[n]=cnt2; for (int i=n; i>=1; i--) if (!id[i]) id[i]=id[i+1]; for (int i=1; i<=n; i++) fa1[i]=i,Size1[i]=1; for (int i=1; i<=q; i++) scanf("%d%d",&ask[i].L,&ask[i].R),ask[i].Time=i; sort(ask+1,ask+q+1,Comp1); int last=1; for (int i=1; i<=cnt2; i++) { int x=fst[i],y=last; while ( y<=q && id[ ask[y].L ]<=i ) y++; if (last==y) continue; sort(ask+last,ask+y,Comp2); for (int j=1; j<=n; j++) fa[j]=j,Size[j]=1; tp=0; int ta=x-1; for (int j=last; j<y; j++) if (ask[j].R>=x) { while (ta<ask[j].R) { ta++; for (edge *p=head[ta]; p; p=p->Next) { int to=p->obj; if ( x<=to && to<=ask[j].R ) Merge(ta,to); } } tail=0; for (int k=Prev[x-1]; k>=ask[j].L; k=Prev[k-1]) for (edge *p=head[k]; p; p=p->Next) { int to=p->obj; if ( ask[j].L<=to && to<=ask[j].R ) Push(k,to); } ans[ ask[j].Time ]=tp; Clear(); } else //当区间没有跨越任何一个关键点的时候,暴力计算答案!!! { for (int k=ask[j].L; k<=ask[j].R; k++) for (edge *p=head[k]; p; p=p->Next) { int to=p->obj; if ( ask[j].L<=to && to<=ask[j].R ) Merge1(k,to); } ans[ ask[j].Time ]=tp1; for (int k=ask[j].L; k<=ask[j].R; k++) { fa1[k]=k,Size1[k]=1; for (edge *p=head[k]; p; p=p->Next) { int to=p->obj; fa1[to]=to,Size1[to]=1; } } tp1=0; } last=y; } for (int i=1; i<=q; i++) printf("%d\n",ans[i]); } return 0;}
- hihocoder1629:Graph (分块+并查集)
- HDU 4677 Query on Graph 并查集+分块
- 【分块】HihoCoder1629+BZOJ4537
- Rikka with Graph(并查集)
- BZOJ4537:[Hnoi2016]最小公倍数 (分块+并查集+启发式合并)
- LA 5031 Graph and Queries (Treap + 并查集)
- Graph Connectivity UVA, 459(并查集)
- HDOJ 题目Graph’s Cycle Component(并查集)
- HDU 5631 Rikka with Graph(并查集)
- HDU 5631 Rikka with Graph (并查集)
- HDU Rikka with Graph (并查集+枚举)
- hdoj Rikka with Graph 5631 (并查集)
- hdu 5631 Rikka with Graph(并查集)
- Graph’s Cycle Component (并查集)
- hdu 5631 Rikka with Graph(并查集)
- hdu 5631 Rikka with Graph(并查集+枚举)
- HDU 5631 Rikka with Graph(并查集)
- hdu 5876 Sparse Graph(BFS+并查集)
- JavaWeb-014-域对象的属性操作
- 告警日志介绍
- eclipse运行报java.lang.OutOfMemoryError: PermGen space解决方法
- Android Studio res文件下新建文件夹不显示怎么办
- HDOJ-3549 Flow Problem (最大流模板)
- hihocoder1629:Graph (分块+并查集)
- VS2015+SDL2+FFmpeg配置
- 数组的初始化两种方式?初始化常见错误
- [Vue][自定义事件]关于组件prop双向绑定的理解和程序调试
- Delcam DentMILL 2015 R1(牙科CAM加工编程系统) 官方版下载
- 【学习C++】学习C++ -> 二维数组
- STM32之中断管理
- 4种jdk自带的常用线程池简单介绍
- bzoj4530 [Bjoi2014]大融合 (LCT维护子树信息)