BZOJ4537 HNOI2016最小公倍数(莫队+并查集)

  考虑边只有一种权值的简化情况。那么当且仅当两点可以通过边权<=x的边连通,且连通块内最大边权为x时,两点间存在路径max为x的路径。可以发现两种权值是类似的,当且仅当两点可以通过边权1<=x且边权2<=y的边连通,且连通块内最大边权1为x、最大边权2为y时,两点间存在路径max为(x,y)的路径。

  一种权值的情况很好处理,从小到大加边并查集维护即可。观察数据范围容易想到根号算法。考虑类似回滚莫队的做法。按边权1将边分块,块内按边权2排序。处理某块时将所有权值1恰好小于该块max的询问找出来按边权2排序,依次处理,每次加入之前块中边权2不大于它的边,然后在块内暴力找满足条件的边加入,使用带撤销并查集即可维护。

  注意存在u=v,a=b=0的询问,所以集合内权值最大值的初值不能设成0。

  调了半天以为有什么细节问题,结果发现并查集整个写错了,居然luogu还有80分数据水爆了啊。

#include<iostream> 
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
#define ll long long
#define N 50010
#define M 100010
#define inf 1000000000
int read()
{
    int x=0,f=1;char c=getchar();
    while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
    while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
    return x*f;
}
char getc(){char c=getchar();while ((c<'A'||c>'Z')&&(c<'a'||c>'z')) c=getchar();return c;}
int gcd(int n,int m){return m==0?n:gcd(m,n%m);}
int n,m,q,t,L[N],R[N],fa[N],mx[N],mxa[N],mxb[N],ans[N],cur[N],size[N],cnt;
int top;
struct data
{
    int x,y,a,b,k,i;
    bool operator <(const data&t) const
    {
        return k<t.k||k==t.k&&b<t.b;
    }
}e[M],a[N],undo[N];
int find(int x){return fa[x]==x?x:find(fa[x]);}
int main()
{
#ifndef ONLINE_JUDGE
    freopen("bzoj4537.in","r",stdin);
    freopen("bzoj4537.out","w",stdout);
    const char LL[]="%I64d\n";
#else
    const char LL[]="%lld\n";
#endif
    n=read(),m=read();
    int block=sqrt(m);
    for (int i=1;i<=m;i++) e[i].x=read(),e[i].y=read(),e[i].a=read(),e[i].b=read(),e[i].k=e[i].a;
    sort(e+1,e+m+1);
    for (int i=1;i<=m;i++)
    {
        e[i].k=(i-1)/block,mx[(i-1)/block]=max(mx[(i-1)/block],e[i].a),R[(i-1)/block]=max(R[(i-1)/block],i);
        if ((i-1)%block==0) L[(i-1)/block]=i;
    }
    cnt=(m-1)/block+1;mx[cnt]=inf+1;R[cnt]=-1;
    sort(e+1,e+m+1);
    q=read();
    for (int i=1;i<=q;i++)
    {
        a[i].x=read(),a[i].y=read(),a[i].a=read(),a[i].b=read(),a[i].i=i;
        for (int j=0;j<=cnt;j++) if (a[i].a<mx[j]) {a[i].k=j;break;}
    }
    sort(a+1,a+q+1);
    int x=0;
    for (int i=0;i<=cnt;i++)
    {
        for (int i=1;i<=n;i++) fa[i]=i,size[i]=1;
        memset(mxa,255,sizeof(mxa));
        memset(mxb,255,sizeof(mxb));
        for (int j=0;j<i;j++) cur[j]=L[j]-1;
        while (x<q&&a[x+1].k==i)
        {
            x++;
            for (int j=0;j<i;j++)
            while (e[cur[j]+1].k==j&&e[cur[j]+1].b<=a[x].b) 
            {
                cur[j]++;
                int p=find(e[cur[j]].x),q=find(e[cur[j]].y);
                if (size[p]<size[q]) swap(p,q);
                if (p!=q) size[p]+=size[q];
                fa[q]=p;mxa[p]=max(max(mxa[p],mxa[q]),e[cur[j]].a),mxb[p]=max(max(mxb[p],mxb[q]),e[cur[j]].b);
            }
            int top=0;
            for (int j=L[i];j<=R[i];j++)
            {
                if (e[j].b>a[x].b) break;
                if (e[j].a<=a[x].a)
                {
                    int p=find(e[j].x),q=find(e[j].y);
                    if (size[p]<size[q]) swap(p,q),swap(e[j].x,e[j].y);
                    top++;undo[top].x=q,undo[top].i=size[p];
                    undo[top].y=p,undo[top].a=mxa[p],undo[top].b=mxb[p];
                    if (p!=q) size[p]+=size[q];
                    fa[q]=p;mxa[p]=max(max(mxa[p],mxa[q]),e[j].a),mxb[p]=max(max(mxb[p],mxb[q]),e[j].b);
                }
            }
            if (find(a[x].x)==find(a[x].y)&&mxa[find(a[x].x)]==a[x].a&&mxb[find(a[x].x)]==a[x].b) ans[a[x].i]=1;
            for (;top;top--)
            {
                mxa[undo[top].y]=undo[top].a,mxb[undo[top].y]=undo[top].b;
                fa[undo[top].x]=undo[top].x;size[undo[top].y]=undo[top].i;
            }
        }
    }
    for (int i=1;i<=q;i++) printf(ans[i]?"Yes\n":"No\n");
    return 0;
}

 

posted @ 2018-11-02 18:00  Gloid  阅读(193)  评论(0编辑  收藏  举报