首页 > 代码库 > BZOJ 4537: [Hnoi2016]最小公倍数

BZOJ 4537: [Hnoi2016]最小公倍数

4537: [Hnoi2016]最小公倍数

Time Limit: 40 Sec  Memory Limit: 512 MB
Submit: 1084  Solved: 400
[Submit][Status][Discuss]

Description

  给定一张N个顶点M条边的无向图(顶点编号为1,2,…,n),每条边上带有权值。所有权值都可以分解成2^a*3^b
的形式。现在有q个询问,每次询问给定四个参数u、v、a和b,请你求出是否存在一条顶点u到v之间的路径,使得
路径依次经过的边上的权值的最小公倍数为2^a*3^b。注意:路径可以不是简单路径。下面是一些可能有用的定义
:最小公倍数:K个数a1,a2,…,ak的最小公倍数是能被每个ai整除的最小正整数。路径:路径P:P1,P2,…,Pk是顶
点序列,满足对于任意1<=i<k,节点Pi和Pi+1之间都有边相连。简单路径:如果路径P:P1,P2,…,Pk中,对于任意1
<=s≠t<=k都有Ps≠Pt,那么称路径为简单路径。

Input

  输入文件的第一行包含两个整数N和M,分别代表图的顶点数和边数。接下来M行,每行包含四个整数u、v、a、
b代表一条顶点u和v之间、权值为2^a*3^b的边。接下来一行包含一个整数q,代表询问数。接下来q行,每行包含四
个整数u、v、a和b,代表一次询问。询问内容请参见问题描述。1<=n,q<=50000、1<=m<=100000、0<=a,b<=10^9

Output

  对于每次询问,如果存在满足条件的路径,则输出一行Yes,否则输出一行 No(注意:第一个字母大写,其余
字母小写) 。

Sample Input

4 5
1 2 1 3
1 3 1 2
1 4 2 1
2 4 3 2
3 4 2 2
5
1 4 3 3
4 2 2 3
1 3 2 2
2 3 2 2
1 3 4 4

Sample Output

Yes
Yes
Yes
No
No

HINT

Source

分析:

貌似每次周末写题我都会产生一种强烈的我是一个智障的感觉...

如果考虑暴力的做法,显然是对于每个询问的$a,b$记录下来,然后把$a<=q.a$&&$b<=q.b$的边加入,用并查集维护连通性和联通快$ab$最大值,然后判断...

但是显然过不了...所以考虑分块,我们把边按照$a$排序,然后分块,再把询问按照$b$排序,枚举每一块,处理询问,取出当前块之前的边,按照$b$排序,然后对于询问把合法的边都加入用并查集维护起来,这个时候如果这条边合法,对于后面的询问也一定合法,接着考虑块内的边,因为$a$是无序的,所以我们必须要支持并查集的回溯操作,所以用一个栈记录并查集,然后对于块内的边每次用完就要回溯,因为要回溯,所以不能路径压缩,要用启发式合并...

我在回溯的时候,一开始写成了酱紫:

fa[x]=x,maxa[y]=a,maxb[y]=b,siz[y]-=siz[x];

  

然后T了一下午...

感谢RYC帮我查错...让我觉得我简直就是智障QAQ~~~

if(x!=y)fa[x]=x,siz[y]-=siz[x];

  

代码:

#include<algorithm>#include<iostream>#include<cstring>#include<cstdio>#include<cmath>//by NeighThorn#define max(a,b) (a>b?a:b)using namespace std; const int maxn=50000+5,maxm=100000+5; int top,fa[maxn],siz[maxn],maxa[maxn],maxb[maxn];int n,m,p,blo,id[maxm],be[maxn],en[maxn],ans[maxn]; struct M{    int x,y,a,b,num;}e[maxm],q[maxn],stk[maxn]; inline int read(void){    char ch=getchar();int x=0;    while(!(ch>=‘0‘&&ch<=‘9‘)) ch=getchar();    while(ch>=‘0‘&&ch<=‘9‘) x=x*10+ch-‘0‘,ch=getchar();    return x;} inline bool cmpa(M xx,M yy){    if(xx.a!=yy.a) return xx.a<yy.a;    return xx.b<yy.b;} inline bool cmpb(M xx,M yy){    if(xx.b!=yy.b) return xx.b<yy.b;    return xx.a<yy.a;} inline int find(int x){    while(fa[x]!=x) x=fa[x];    return x;} inline void merge(int x,int y,int a,int b){    int fx=find(x),fy=find(y);    if(siz[fx]>siz[fy])        swap(fx,fy);    top++;    stk[top].x=fx,stk[top].y=fy;    stk[top].a=maxa[fy],stk[top].b=maxb[fy];    maxa[fy]=max(a,max(maxa[fx],maxa[fy]));    maxb[fy]=max(b,max(maxb[fx],maxb[fy]));    if(fx==fy) return;    fa[fx]=fy;siz[fy]+=siz[fx];} signed main(void){    n=read(),m=read();blo=sqrt(m*log2(m));    for(int i=1;i<=m;i++)        e[i].x=read(),e[i].y=read(),e[i].a=read(),e[i].b=read();    for(int i=1;i<=m;i++) id[i]=(i-1)/blo+1;    sort(e+1,e+m+1,cmpa);p=read();    for(int i=1;i<=id[m];i++)        be[i]=lower_bound(id+1,id+m+1,i)-id,        en[i]=upper_bound(id+1,id+m+1,i)-id-1;    for(int i=1;i<=p;i++)        q[i].x=read(),q[i].y=read(),q[i].a=read(),q[i].b=read(),q[i].num=i;    sort(q+1,q+p+1,cmpb);    for(int i=1;i<=id[m];i++){        int st=1,A=e[be[i]].a,AA=e[en[i]+1].a;        sort(e+1,e+be[i]+1,cmpb);        for(int j=1;j<=n;j++)             fa[j]=j,siz[j]=1,maxa[j]=maxb[j]=-1;        for(int j=1;j<=p;j++)            if(q[j].a>=A&&(i==id[m]||q[j].a<AA)){                for(;st<be[i]&&q[j].b>=e[st].b;st++)                    merge(e[st].x,e[st].y,e[st].a,e[st].b);                top=0;                 for(int k=be[i];k<=en[i];k++)                    if(e[k].a<=q[j].a&&e[k].b<=q[j].b)                        merge(e[k].x,e[k].y,e[k].a,e[k].b);                int fx=find(q[j].x),fy=find(q[j].y);                ans[q[j].num]=(fx==fy&&maxa[fx]==q[j].a&&maxb[fx]==q[j].b);                while(top){                    int x=stk[top].x,y=stk[top].y;                    maxa[y]=stk[top].a,maxb[y]=stk[top].b;                     if(x!=y)fa[x]=x,siz[y]-=siz[x];                    top--;                }            }    }    for(int i=1;i<=p;i++) puts(ans[i]?"Yes":"No");    return 0;}

  


By NeighThorn

BZOJ 4537: [Hnoi2016]最小公倍数