BZOJ4537:[Hnoi2016]最小公倍数 (分块+并查集+启发式合并)
来源:互联网 发布:诸葛武侯巧连神数 算法 编辑:程序博客网 时间:2024/06/06 19:41
题目传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=4537
题目分析:神题一道,一开始我YY了一下LCT,发现不可做,后来看了网上大神的做法发现是分块……
由于2和3互质,所以我们可以看成一条边有两个属性a,b。先考虑暴力怎么做:对于一个询问(u,v,A,B),我们将所有
做过类似的题目的人都知道:当一条边有两种属性的时候,我们通常要对一个属性进行排序,然后对另一个属性用数据结构之类的东西优化,以降低时间复杂度。现在我们将边按a属性从小到大排序,然后分成
1.我们先找出所有
由于这些询问的A值都大于等于第h条边的a值,它也必定大于等于第1~h-1条边的a值,所以第1~h-1条边的a值都是满足要求的,它们只需要考虑b值是否小于等于这些询问的B值就行,于是:
2.将第1~h-1条边和这些询问ask按b(B)值堆在一起排序。
然后我们按b值从小到大做。对于一条边,我们直接将它加进并查集里;对于一个询问,我们先暴力扫一遍第h~t条边(因为这些边可能也合法),如果有
但上述方法是无法保证时间复杂度的,因为一次询问可能在做多个块的时候被处理到(比如所有边的a值和所有询问的A值都相等)。所以要加一个小优化:如果第h,t,t+1条边的a值都相等,就不处理这个块。本质上就是在所有a值相同的边中,我们只会处理最后一条边所在的块。这样不会影响正确性,而且保证了每一个询问只会在做一个块的时候被处理到,于是时间复杂度为
#include<iostream>#include<string>#include<cstring>#include<cmath>#include<cstdio>#include<cstdlib>#include<stdio.h>#include<algorithm>using namespace std;const int maxn=50010;const int oo=1000000001;struct data{ int u,v,a,b,Time;} e[maxn<<1];data ask[maxn];data sak[maxn];int tail=0;int bot[maxn*3];int fa[maxn];int Size[maxn];int ma[maxn];int mb[maxn];int ans[maxn];int n,m,q,sm;bool Comp1(data x,data y){ return x.a<y.a || ( x.a==y.a && x.b<y.b );}int Get(int x){ if (x<=m) return e[x].b; return ask[x-m].b;}bool Comp2(int x,int y){ int p=Get(x),q=Get(y); return ( p<q || ( p==q && x<=m && y>m ) );}int Find(int x){ if (x==fa[x]) return x; return Find(fa[x]);}void Add(int x){ int y=Find(e[x].u),z=Find(e[x].v); if (y!=z) { if (Size[y]>Size[z]) swap(y,z); fa[y]=z; Size[z]+=Size[y]; ma[z]=max(ma[z],ma[y]); mb[z]=max(mb[z],mb[y]); } ma[z]=max(ma[z],e[x].a); mb[z]=max(mb[z],e[x].b);}void Insert(int x){ int y=Find(e[x].u),z=Find(e[x].v); tail++; if (Size[y]>Size[z]) swap(y,z); sak[tail].v=z; sak[tail].a=ma[z]; sak[tail].b=mb[z]; if (y==z) sak[tail].u=0; else { Size[z]+=Size[y]; fa[y]=z; sak[tail].u=y; ma[z]=max(ma[z],ma[y]); mb[z]=max(mb[z],mb[y]); } ma[z]=max(ma[z],e[x].a); mb[z]=max(mb[z],e[x].b);}void Delete(){ while (tail) { if (sak[tail].u) { Size[ sak[tail].v ]-=Size[ sak[tail].u ]; fa[ sak[tail].u ]=sak[tail].u; } ma[ sak[tail].v ]=sak[tail].a; mb[ sak[tail].v ]=sak[tail].b; tail--; }}int main(){ freopen("4537.in","r",stdin); freopen("4537.out","w",stdout); scanf("%d%d",&n,&m); for (int i=1; i<=m; i++) scanf("%d%d%d%d",&e[i].u,&e[i].v,&e[i].a,&e[i].b); sort(e+1,e+m+1,Comp1); e[m+1].a=oo; scanf("%d",&q); for (int i=1; i<=q; i++) scanf("%d%d%d%d",&ask[i].u,&ask[i].v,&ask[i].a,&ask[i].b),ask[i].Time=i; sort(ask+1,ask+q+1,Comp1); ask[q+1].a=oo; sm=(int)floor( sqrt( (double)m )+1e-7 ); int he=1,ta=0; for (int i=1; i<=m; i+=sm) { int j=min(i+sm-1,m); if ( e[i].a==e[j].a && e[j].a==e[j+1].a ) continue; while (ask[he].a<e[i].a) he++; while (ask[ta+1].a<=e[j].a) ta++; if (he<=ta) { for (int k=1; k<i; k++) bot[k]=k; for (int k=he; k<=ta; k++) bot[i+k-he]=m+k; sort(bot+1,bot+(i+ta-he)+1,Comp2); for (int k=1; k<=n; k++) fa[k]=k,Size[k]=1,ma[k]=mb[k]=-1; for (int k=1; k<=i+ta-he; k++) if (bot[k]<=m) Add(bot[k]); else { int x=bot[k]-m; for (int w=i; w<=j; w++) if ( e[w].a<=ask[x].a && e[w].b<=ask[x].b ) Insert(w); int y=Find(ask[x].u),z=Find(ask[x].v); if ( y!=z || ma[y]<ask[x].a || mb[y]<ask[x].b ) ans[ ask[x].Time ]=1; else ans[ ask[x].Time ]=2; Delete(); } } } for (int i=1; i<=q; i++) if (ans[i]<2) printf("No\n"); else printf("Yes\n"); return 0;}
- BZOJ4537:[Hnoi2016]最小公倍数 (分块+并查集+启发式合并)
- 【bzoj4537】【HNOI2016】【最小公倍数】【并查集+启发式合并+分块】
- BZOJ4537 [ HNOI2016 ] 最小公倍数 (按轶合并带权并查集+分块离线)
- BZOJ4537 [Hnoi2016]最小公倍数 分块+可撤销并查集
- [BZOJ4537][Hnoi2016][分块]最小公倍数
- BZOJ4537 [Hnoi2016]最小公倍数 (可撤销&&可持久化并查集学习笔记)
- [BZOJ4537] [HNOI/AHOI2016] 最小公倍数 - 分块 - 并查集
- [分块 并查集] BZOJ 4537 [Hnoi2016]最小公倍数
- bzoj 4537: [Hnoi2016]最小公倍数 并查集按秩合并+分块
- BZOJ4537: [Hnoi2016]最小公倍数
- bzoj4537: [Hnoi2016]最小公倍数
- bzoj4537: [Hnoi2016]最小公倍数
- bzoj4537【HNOI2016】最小公倍数
- bzoj4537 [Hnoi2016]最小公倍数
- [bzoj4537][hnoi2016]最小公倍数
- BZOJ4537[HNOI2016]最小公倍数
- hdu6109(并查集+启发式合并)
- [BZOJ2733][HNOI2012]永无乡(并查集+splay启发式合并)
- PAT 1053. Path of Equal Weight (30)
- 用Python和Pygame写游戏-从入门到精通(9) Vector2d
- 树莓派3b连接GPS+BD模块并用python获取数据(串口版)
- 数据库连接工具Navicat Premium_11.2.7简体中文版
- CUDA 学习笔记 1
- BZOJ4537:[Hnoi2016]最小公倍数 (分块+并查集+启发式合并)
- codeforces 279-B. Books(尺取)
- git创建分支
- .net 手机滑动加载
- rman channel是无限制的吗?
- 使用PowerDesigner设计数据库物理模型
- CentOs 7 64位 安装Navicat
- linux dig
- 线程不安全的实体Bean