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

本文介绍了一种使用哈希和启发式合并算法解决图论问题的方法。通过维护每个图中点的联通块编号,并利用哈希技术快速判断两点是否联通,实现了高效处理大量边的加入操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

好题。好像get了hash的新姿势。我好弱啊。


我们可以用fi,j表示第i个图中点j所属联通块的编号,初始fi,j=j,然后每次连边时,我们可以进行启发式合并,合并的复杂度为O(dnlogn)
如果两个点在所有图中的所属联通块编号都相同,则这两个点是联通的,我们可以hash来判一下,令hashi表示点ihash值,是通过对数组fj,i哈希求出来的(并且为了方便我们支持加减某一位的哈希值)。如果hashi=hashj则证明ij是联通的。我们可以对hash数组再做一次hash,用hashi and U表示hashi的哈希值,U是一个二进制下每位都是1的数,然后在得到的值下面挂一个数组模拟链表就好了。
据CA爷说卡unsigned int。

#include<iostream>
#include<cstdio>
#define ll unsigned long long 
#define U 262143
#define N 5005
#define D 205
using namespace std;
int fa[D][N],s[D][N],head[D][N],next[D*N*2],list[D*N*2];
ll power[D],hash[N],val[N];
int head_[U+1],size[N],next_[N],res[N];
int n,m,d,cur,ans,cnt;
inline int read()
{
    int a=0,f=1; char c=getchar();
    while (c<'0'||c>'9') {if (c=='-') f=-1; c=getchar();}
    while (c>='0'&&c<='9') {a=a*10+c-'0'; c=getchar();}
    return a*f;
}
inline void Addedge(int k,int x,int y)
{
    next[++cnt]=head[k][x];
    head[k][x]=cnt;
    list[cnt]=y;
}
inline void insert(ll v)
{
    int x=v&U;
    for (int i=head_[x];i;i=next_[i])
        if (val[i]==v) 
        {
            ans+=2*size[i]+1;
            size[i]++;
            return;
        }
    next_[res[cur]]=head_[x]; head_[x]=res[cur]; val[res[cur]]=v; size[res[cur]]=1;
    ans++; cur--;
}
inline void remove(ll v)
{
    int x=v&U;
    if (val[head_[x]]==v)
    {
        ans-=size[head_[x]]*2-1;
        if (--size[head_[x]]==0) res[++cur]=head_[x],head_[x]=next_[head_[x]];
        return;
    }
    for (int i=next_[head_[x]],j=head_[x];i;j=i,i=next_[i])
        if (val[i]==v)
        {
            ans-=size[i]*2-1;
            if (--size[i]==0) res[++cur]=i,next[j]=next[i];
            return;
        }
}
void dfs(int k,int x,int f,int ff)
{
    remove(hash[x]);
    hash[x]-=power[k]*fa[k][x];
    fa[k][x]=ff;
    hash[x]+=power[k]*fa[k][x];
    insert(hash[x]);
    for (int i=head[k][x];i;i=next[i])
        if (list[i]!=f) dfs(k,list[i],x,ff);
}
inline void unite(int k,int x,int y)
{
    if (fa[k][x]==fa[k][y]) return;
    if (s[k][fa[k][x]]>s[k][fa[k][y]]) swap(x,y);
    s[k][fa[k][y]]+=s[k][fa[k][x]]; 
    Addedge(k,x,y); Addedge(k,y,x);
    dfs(k,x,y,fa[k][y]);
}
int main()
{
    d=read(); n=read(); m=read(); power[0]=1;
    for (int i=1;i<=d;i++) power[i]=power[i-1]*2333;
    for (int i=1;i<=n;i++) res[++cur]=i;
    for (int i=1;i<=n;i++)
    {
        for (int j=1;j<=d;j++)
            fa[j][i]=i,s[j][i]=1,hash[i]+=power[j]*i;
        insert(hash[i]);
    }
    for (int i=1;i<=m;i++)
    {
        int u=read(),v=read(),w=read();
        unite(w,u,v);
        printf("%d\n",ans);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值