dtoj#4222. 小b爱旅行(travel)

题目描述:

小 $b$ 有一张无向图。

小 $b$ 可以从点 $1$ 出发,走任意一条路径,她将得到路径上的边的边权的异或值(一条边可能被经过多次,这时经过 几次就异或几次)。

路径可以为空,这时异或值为 $0$ 。

小 $b$ 想知道她能得到多少个不同的异或值。

小 $b$ 还会删掉 $q$ 条边,每删一条边,她都会问你一次。

数据范围:

对于全部数据:

$\bullet~1 \le n \le 10^5,1 \le m \le 2 \times 10^5,0 \le q \le m$;

$\bullet~1 \le x,y \le n,0 \le w \le 10^{18}$;

$\bullet~1 \le i \le m$且所有 $i$ 互不相同;

保证不存在重边和自环。

对于 $10 \%$ 的数据,$m \le 10$ 且 $q=0$ ;

对于另外 $30 \%$ 的数据,$w \le 1000$ 且 $q=0$ ;

对于另外 $30 \%$ 的数据,$q=0$ 。

算法标签:线性基

思路:

考虑对于一个环,是可以和任何一条路径异或得到的新的路径的。因为来回环上的点经过一次,来的路上经过的没有回路的路径,再回去的时候再走一次。

所以我们把环挑出来断成链,变成一棵生成树,跑出这棵树上的答案,然后把环的答案存进线性基中。

考虑如何把路径答案乘环的答案重复的时候,考虑对于线性基为1且路径答案为1的答案和对应为异或,最后得到的值互不相同,所有答案就会不相同。

对于断链,我们把他反过来做,当成是不断加边,那么每次对于没有加到1联通块的不做修改,对于新加的与1相连的点,重新dfs,如果对线性基造成改变,就重新统计所有点的答案。

效率是 $O(n\times3600\times logn)$   这个 $log$ 是 $map$ 的 $log$ 改成 $hash$ 表就可以省去这个 $logn$ ,但是因为数据没跑满能过,就这样吧。

以下代码:

#include<bits/stdc++.h>
#define il inline
#define LL long long
#define _(d) while(d(isdigit(ch=getchar())))
using namespace std;
const int N=1e5+5,M=4e5+5;
bool pd,tag[M];
LL w[M],dist[N],val[N],res[M],b[80],num;
map<LL,bool> ma;
int cnt,qr[M],tot;
int n,m,q,head[N],ne[M],to[M],d[N],f[N];
il LL read(){
    LL x;char ch;_(!);x=ch^48;
    _()x=(x<<1ll)+(x<<3ll)+(ch^48);
    return x;
}
il void ins(int x,int y,LL z){
    ne[++cnt]=head[x];head[x]=cnt;
    to[cnt]=y;w[cnt]=z;
}
il int getfa(int x){
    if(f[x]==x)return x;
    return f[x]=getfa(f[x]);
}
il void insert(LL x){
    for(int i=60;i>=0;i--){
        if(x&(1ll<<i)){
            if(b[i])x^=b[i];
            else{
                b[i]=x;tot++;
                pd=1;break;
            }
        }
    }
}
il void dfs1(int x,int fa){
    f[getfa(x)]=getfa(1);
    for(int i=head[x];i;i=ne[i]){
        if(fa==to[i]||tag[i])continue;
        if(!d[to[i]]){
            dist[to[i]]=dist[x]^w[i];
            d[to[i]]=d[x]+1;dfs1(to[i],x);
        }
        else{
            insert(dist[x]^dist[to[i]]^w[i]);
        }
    }
}
il LL C(LL x){
    for(int i=60;i>=0&&x;i--){
        if(b[i]&&(x>>i&1))x^=b[i];
    }
    return x;
}
il void add(int x){
    val[x]=C(dist[x]);
    if(!ma[val[x]])num++,ma[val[x]]=1;
}
il void dfs2(int x,int fa){
    f[getfa(x)]=getfa(1);add(x);
    for(int i=head[x];i;i=ne[i]){
        if(fa==to[i]||tag[i])continue;
        if(!d[to[i]]){
            dist[to[i]]=dist[x]^w[i];
            d[to[i]]=d[x]+1;
            dfs2(to[i],x);
        }
        else{
            insert(dist[x]^dist[to[i]]^w[i]);
        }
    }
}
il void rebuild(){
    num=0;
    for(int i=1;i<=n;i++)if(d[i])ma[val[i]]=0;
    for(int i=1;i<=n;i++)if(d[i])add(i);
}
int main()
{
    n=read();m=read();q=read();
    for(int i=1;i<=n;i++)f[i]=i;
    for(int i=1;i<=m;i++){
        int x=read(),y=read();LL w=read();
        ins(x,y,w);ins(y,x,w);
    }
    for(int i=1;i<=q;i++){
        int x=read();qr[i]=x;
        tag[x<<1]=tag[(x<<1)-1]=1;
    }
    d[1]=1;dfs1(1,0);
    for(int i=1;i<=n;i++)if(d[i])add(i);
    for(int i=q;i;i--){
        res[i]=(1ll<<tot)*num;tag[qr[i]<<1]=tag[(qr[i]<<1)-1]=0;
        int a=to[qr[i]<<1],b=to[(qr[i]<<1)-1];
        int x=getfa(a),y=getfa(b);
        if(x==y){
            if(x^getfa(1))continue;
            pd=0;
            insert(dist[a]^dist[b]^w[qr[i]<<1]);
            if(pd)rebuild();
        }
        else{
            if(y==getfa(1))swap(x,y),swap(a,b);
            f[y]=x;
            if(x^getfa(1))continue;
            dist[b]=dist[a]^w[qr[i]<<1];
            pd=0;d[b]=d[a]+1;dfs2(b,a);
            if(pd)rebuild();
        }
    }
    res[0]=(1ll<<tot)*num;
    for(int i=0;i<=q;i++)printf("%lld\n",res[i]);
    return 0;
}
View Code

 

转载于:https://www.cnblogs.com/Jessie-/p/10454416.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值