bzoj 4298: [ONTAK2015]Bajtocja hash启发式合并

题意

给定d张无向图,每张图都有n个点。一开始,在任何一张图中都没有任何边。接下来有m次操作,每次操作会给出a,b,k,意为在第k张图中的点a和点b之间添加一条无向边。你需要在每次操作之后输出有序数对(a,b)的个数,使得1<=a,b<=n,且a点和b点在d张图中都连通。
1<=d<=200,1<=n<=5000,1<=m<=1000000

分析

一开始的想法是,先把操作离线下来,那么每个点对有贡献的一定是一个询问后缀。那么我们可以对每个图维护一个并查集,当两个并查集合并时,加入的新边的权值为当前操作编号。那么一个点对开始有贡献的时刻就是它们在d个图中路径上权值的最大值。这样做复杂度是 O ( d n 2 ) 的,显然过不了。
正解的话,设 f i , j 表示点 i 在第 j 个图中的连通块编号,这样每次加边的时候就可以用启发式合并来维护;同时用一个 h i 表示 f i , j 的哈希值,那么用一个哈希表来资辞插入删除并且维护答案就好了。
时间复杂度 O ( m + n d l o g n )

代码

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>

typedef unsigned long long ull;

const int N=5005;
const int D=205;
const int MOD=2333333;
const int seed=2333;

int n,m,d,cnt,last[N][D],f[N][D],g[N][D],ans;
ull hash[N],po[D];
struct edge{int to,next;}e[N*D*2];

struct Hash_Table
{
    int cnt[N],nx[N],ls[N],st[MOD],top,stack[N];
    ull id[N];

    void ins(ull x)
    {
        int y=x%MOD;
        for (int i=st[y];i;i=nx[i])
            if (id[i]==x)
            {
                ans+=cnt[i]*2+1;
                cnt[i]++;
                return;
            }
        int z=stack[top--];
        cnt[z]=1;id[z]=x;ans++;
        ls[st[y]]=z;nx[z]=st[y];st[y]=z;
    }

    void del(ull x)
    {
        int y=x%MOD;
        for (int i=st[y];i;i=nx[i])
            if (id[i]==x)
            {
                cnt[i]--;
                ans-=cnt[i]*2+1;
                if (!cnt[i])
                {
                    stack[++top]=i;
                    if (ls[i]) nx[ls[i]]=nx[i];
                    if (nx[i]) ls[nx[i]]=ls[i];
                    if (st[y]==i) st[y]=nx[i];
                }
                break;
            }
    }
}ha;

int read()
{
    int x=0,f=1;char ch=getchar();
    while (ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while (ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}

void addedge(int x,int k,int y)
{
    e[++cnt].to=y;e[cnt].next=last[x][k];last[x][k]=cnt;
}

void dfs(int x,int y,int k)
{
    ha.del(hash[x]);
    hash[x]-=(ull)po[k]*f[x][k];
    f[x][k]=y;
    hash[x]+=(ull)po[k]*f[x][k];
    ha.ins(hash[x]);
    for (int i=last[x][k];i;i=e[i].next)
        if (f[e[i].to][k]!=y) dfs(e[i].to,y,k);
}

int main()
{
    d=read();n=read();m=read();
    po[0]=1;
    for (int i=1;i<=d;i++) po[i]=(ull)po[i-1]*seed;
    for (int i=1;i<=n;i++) ha.stack[++ha.top]=i;
    for (int i=1;i<=n;i++)
        for (int j=1;j<=d;j++)
            f[i][j]=i,g[i][j]=1,hash[i]+=(ull)f[i][j]*po[j];
    for (int i=1;i<=n;i++) ha.ins(hash[i]);
    while (m--)
    {
        int x=read(),y=read(),k=read();
        if (f[x][k]==f[y][k]) {printf("%d\n",ans);continue;}
        if (g[f[x][k]][k]>g[f[y][k]][k]) std::swap(x,y);
        g[f[y][k]][k]+=g[f[x][k]][k];
        dfs(x,f[y][k],k);
        addedge(x,k,y);addedge(y,k,x);
        printf("%d\n",ans);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_33229466/article/details/80961469