【分块】HihoCoder1629+BZOJ4537

HIHO1629 : Graph

原题地址

【题目大意】
给出一幅n个点,m条边的无向图,然后给出q组询问。每组询问给定一个区间[L,R],问[L,R]中有多少点对可以相互到达。可以到达的要求是只能走[L,R]中的点。

不超过5组数据,n,m<=50000,q<=100000。
单点时限:4000ms
内存限制:256MB

【题目分析】
分块+莫队

【解题思路】
将询问排序后分块——

当我们枚举第i个关键点为左端点的时候,同时处理所有左端点在(第i-1个关键点,第i个关键点],右端点在第i个关键点及之后的询问,将这些询问按右端点从小到大排序。假设当前的询问区间是[L,R],则我们先将[第i个关键点,R]的边加进并查集,这个部分可以用启发式合并+路径压缩。然后将[L,第i个关键点)的边用启发式合并加进并查集(不能用路径压缩,因为要撤销)。

如果某个询问没有跨越任何一个关键点,就说明区间内的边数小于 m ,直接用启发式合并+路径压缩的并查集处理即可。处理完了之后再将修改了的元素暴力初始化。

时间复杂度 O((n+q)mlog(n))

【代码】

#include
#include
#include
#include
using namespace std;

typedef long long LL;
const int MAXN=2e5+10;
const int S=233;
int n,m,q,T;
int tot,tota,qcnt,cnt;
int f[MAXN],st[MAXN],ed[MAXN],pre[MAXN],l[MAXN],r[MAXN];
int stid[S*2+10],sts[S*2+10],stf[S*2+10];
int head[MAXN],headq[MAXN],s[MAXN];

struct Tway
{
    int u,v,nex;
};
Tway e[MAXN<<1],a[MAXN<<1],qs[MAXN<<1];

inline void adde(int u,int v)
{
    ++tot;
    e[tot].u=u;e[tot].v=v;
    e[tot].nex=head[u];head[u]=tot;
}

inline void adda(int u,int v)
{
    ++tota;
    a[tota].u=u;a[tota].v=v;
}

inline void addway(int u,int v)
{
    ++qcnt;
    qs[qcnt].u=u;qs[qcnt].v=v;
    qs[qcnt].nex=headq[u];headq[u]=qcnt;
}

inline void _reset()
{
    memset(head,0,sizeof(head));
    memset(headq,0,sizeof(headq));
    tot=tota=cnt=qcnt=0;
}

inline int findf(int x)
{
    return x==f[x]? x : f[x]=findf(f[x]);
}

inline int ofindf(int x)
{
    return x==f[x]? x : ofindf(f[x]);
}

int main()
{
    freopen("C.in","r",stdin);
    freopen("C.out","w",stdout);

    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d%d",&n,&m,&q);

        for(int i=1;i<=m;++i)
        {
            int u,v;
            scanf("%d%d",&u,&v);
            adde(u,v);adde(v,u);
        }
        for(int i=1;i<=n;++i)
        {
            st[i]=tota+1;
            for(int j=head[i];j;j=e[j].nex)
                adda(i,e[j].v);
            ed[i]=tota;
        }
        for(int i=1;i<=q;++i)
        {
            scanf("%d%d",&l[i],&r[i]);
            pre[i]=-1;
            addway(i,headq[ed[r[i]]]);
        }

        for(int sta=S;sta<=tota;sta+=S)
        {
            for(int i=1;i<=n;++i)
            {
                f[i]=i;
                s[i]=1;
            }

            int ans=0;
            for(int j=sta+1;sta<=tota;++j)
            {
                if(a[sta].u<=a[j].v && a[j].v<=a[j].u)
                {
                    int fx=findf(a[j].u),fy=findf(a[j].v);
                    if(fx!=fy)
                    {
                        ans-=(1ll*s[fx]*(s[fx]-1)/2+1ll*s[fy]*(s[fy]-1)/2);
                        s[fy]+=s[fx];f[fx]=fy;
                        ans+=1ll*s[fy]*(s[fy]-1)/2;
                    }
                }

                for(int k=headq[j];k;k=qs[k].nex)
                {
                    int tmp=st[l[qs[k].v]];
                    if(tmp<=sta-S || stacontinue;
                    int tmpans=ans,stn=0;

                    for(int i=tmp;i<=sta;++i)
                    {
                        if(l[qs[k].v]<=a[i].v && a[i].v<=r[qs[k].v])
                        {
                            int fx=ofindf(a[i].u),fy=ofindf(a[i].v);
                            if(fx!=fy)
                            {
                                if(s[fx]>s[fy])
                                    swap(fx,fy);
                                stid[++stn]=fx;stf[stn]=f[fx];sts[stn]=s[fx];
                                stid[++stn]=fy;stf[stn]=f[fy];sts[stn]=s[fy];
                                tmpans-=(1ll*s[fx]*(s[fx]-1)/2+1ll*s[fy]*(s[fy]-1));
                                s[fy]+=s[fx];f[fx]=fy;
                                tmpans+=1ll*s[fy]*(s[fy]-1)/2;
                            }
                        }   
                    }

                    pre[qs[k].v]=tmpans;
                    for(int i=stn;i;--i)
                    {
                        f[stid[i]]=stf[i];
                        s[stid[i]]=sts[i];
                    }
                }
            }
        }

        for(int qq=1;qq<=q;++qq)
            if(pre[qq]!=-1)
                printf("%d\n",pre[qq]);
            else
            {
                int ans=0;
                for(int i=l[qq];i<=r[qq];++i)
                {
                    f[i]=i;
                    s[i]=1;
                }
                for(int i=l[qq];i<=r[qq];++i)
                {
                    for(int j=head[i];j;j=e[j].nex)
                    {
                        if(l[qq]<=e[j].v && e[j].v<=r[qq])
                        {
                            int fx=findf(i),fy=findf(e[j].v);
                            if(fx!=fy)
                            {
                                ans-=(1ll*s[fx]*(s[fx]-1)/2+1ll*s[fy]*(s[fy]-1)/2);
                                s[fy]+=s[fx];f[fx]=fy;
                                ans+=1ll*s[fy]*(s[fy]-1)/2;
                            }
                        }
                    }
                }
                printf("%d\n",ans);
            }
    }

    return 0;
}

BZOJ4537

原题地址

题目描述
Description
 给定一张N个顶点M条边的无向图(顶点编号为1,2,…,n),每条边上带有权值。
所有权值都可以分解成 2a×3b 的形式。

现在有q个询问,每次询问给定四个参数u、v、a和b,请你求出是否存在一条顶点u到v之间的路径,使得路径依次经过的边上的权值的最小公倍数为 2a×3b
注意:路径可以不是简单路径。

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

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

【解题思路】
转化一下就是问是否存在求一条路径使得a的最大值和b的最大值分别为给定的值。

我们将边权按a排序,每 m 分为1块。然后对于每一块,我们处理a在该块内的所有询问。假设我们现在处理第i块,我们先将第i块的询问及1至i-1块的边取出,按b排序。然后按顺序加入这些边,同时我们要用带权并查集维护图的连通性以及联通块中的a,b的最大值。对于一个询问,可能还存在一些合法的边在第i块中,但这些边不超过 m 条,我们可以暴力加入再暴力还原。
时间复杂度: O(mmlogm)

【代码】

#include
#include
#include
#include
using namespace std;

const int lim=332;
const int MAXN=1e5+10;

int n,m,qs,cnt,co;
int ans[MAXN],ida[MAXN],idb[MAXN];
int fa[MAXN],d[MAXN],va[MAXN],vb[MAXN];

struct pat
{
    int x,y,z;
    pat(){}
    pat(int _x,int _y,int _z){x=_x,y=_y;z=_z;}
};
pat opt[MAXN];

struct Tway
{
    int x,y,a,b;
};
Tway e[MAXN],q[MAXN];

inline bool cmpa(const Tway&A,const Tway&B)
{
    return A.aint A,int B)
{
    return e[A].bq(int A,int B)
{
    return q[A].b<q[B].b;
}

inline int findf(int x)
{
    return x==fa[x]? x : findf(fa[x]);
}

inline void merge(int x,int y,int A,int B,int typ)
{
    x=findf(x);y=findf(y);
    if(x!=y)
    {
        if(d[x]==d[y])
        {
            if(typ)
                opt[++co]=pat(1,x,d[x]);
            d[x]++;
        }

        if(d[x]y])
            swap(x,y);
        if(typ)
            opt[++co]=pat(0,y,y);
        fa[y]=x;

        if(va[x]y] && va[y]>A)
        {
            if(typ)
                opt[++co]=pat(2,x,va[x]);
            va[x]=va[y];
        }
        if(vb[x]y] && vb[y]>B)
        {
            if(typ)
                opt[++co]=pat(3,x,vb[x]);
            vb[x]=vb[y];
        }
    }
    if(va[x]if(typ)
            opt[++co]=pat(2,x,va[x]);
        va[x]=A;
    }
    if(vb[x]if(typ)
            opt[++co]=pat(3,x,vb[x]);
        vb[x]=B;
    }
}

inline void remark()
{
    for(int i=co;i>=1;--i)
    {
        if(!opt[i].x)
            fa[opt[i].y]=opt[i].z;
        else
        if(opt[i].x==1)
            d[opt[i].y]=opt[i].z;
        else
        if(opt[i].x==2) 
            va[opt[i].y]=opt[i].z;
        else
            vb[opt[i].y]=opt[i].z;
    }
    co=0;
}

int main()
{
    freopen("BZOJ4537.in","r",stdin);
    freopen("BZOJ4537.out","w",stdout);

    scanf("%d%d",&n,&m);
//  lim=(int)floor(sqrt(m-1));
    for(int i=1;i<=m;++i)
    {
        scanf("%d%d%d%d",&e[i].x,&e[i].y,&e[i].a,&e[i].b);
        ida[i]=i;
    }
    sort(e+1,e+m+1,cmpa);

    scanf("%d",&qs);
    for(int i=1;i<=qs;++i)
        scanf("%d%d%d%d",&q[i].x,&q[i].y,&q[i].a,&q[i].b);
    for(int i=0;i<=m;i+=lim)
    {
        cnt=0;
        for(int j=1;j<=qs;++j)
            if(q[j].a>=e[i].a && (i+lim>m || q[j].aif(!cnt)
            continue;
        sort(ida+1,ida+i+1,cmpe);
        sort(idb+1,idb+cnt+1,cmpq);

        for(int j=1;j<=n;++j)
        {
            fa[j]=j;d[j]=0;
            va[j]=vb[j]=-1;
        }
        for(int j=1,k=1;j<=cnt;++j)
        {
            while(k<=i && e[ida[k]].b<=q[idb[j]].b)
            {
                merge(e[ida[k]].x,e[ida[k]].y,e[ida[k]].a,e[ida[k]].b,0);
                ++k;
            }

            co=0;
            for(int t=min(i+lim-1,m);t>i;--t)
                if(e[t].a<=q[idb[j]].a && e[t].b<=q[idb[j]].b)
                    merge(e[t].x,e[t].y,e[t].a,e[t].b,1);

            int x=findf(q[idb[j]].x),y=findf(q[idb[j]].y);
            if(x==y && va[x]==q[idb[j]].a && vb[x]==q[idb[j]].b)
                ans[idb[j]]=1;
            remark();
        }
    }
    for(int i=1;i<=qs;++i)
        (ans[i] ? puts("Yes") : puts("No"));
    return 0;
}

你可能感兴趣的:(分而治之-分块)