BZOJ 4537: [Hnoi2016]最小公倍数

Posted NeighThorn

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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]最小公倍数的主要内容,如果未能解决你的问题,请参考以下文章

BZOJ4537[Hnoi2016]最小公倍数 分块

bzoj4537: [Hnoi2016]最小公倍数

4537: [Hnoi2016]最小公倍数|分块

最小公倍数

[bzoj4540][Hnoi2016]序列——单调栈+莫队+RMQ

BZOJ4540HNOI2016序列(莫队)