[高斯消元 线性基 树 记数] BZOJ 2322 [BeiJing2011]梦想封印

删边变加边 

如果加的是非树边 相当于加了一个环 加入线性基中

如果加入的是树边 就dfs扩展树

两条链是等价的 相当于 他们用线性基消过后是一样的 用set维护

解是不等价链的个数*1<<线性基的个数-1

减一是要去掉0


#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<set>
using namespace std;
typedef long long ll;

inline char nc(){
    static char buf[100000],*p1=buf,*p2=buf;
    if (p1==p2) { p2=(p1=buf)+fread(buf,1,100000,stdin); if (p1==p2) return EOF; }
    return *p1++;
}
inline void read(int &x){
    char c=nc(),b=1;
    for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1;
    for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b;
}
inline void read(ll &x){
    char c=nc(),b=1;
    for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1;
    for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b;
}

const int N=5005;
const int M=20005;
const int K=63;

struct edge{
    int u,v,next; ll w;
}G[M<<1]; int can[M<<1];
int head[N],inum=1;
inline void add(int u,int v,ll w,int p){
    G[p].u=u; G[p].v=v; G[p].w=w; G[p].next=head[u]; head[u]=p;
}

int n,m;
ll depth[N]; int vst[N];
ll a[M<<1]; int tot,base[K+5];

inline ll Ext(ll x){
    for (int p=K;~p;p--)
        if (x>>p&1)
            if (base[p])
                x^=a[base[p]];
    return x;
}

inline void Ins(ll x){
    for (int p=K;~p;p--)
        if (x>>p&1)
            if (base[p])
                x^=a[base[p]];
            else{
                a[base[p]=++tot]=x; return;
            }
}

ll chain[N]; int cnt;

inline ll calc()
{
    set<ll> Set;
    int pnt=0;
    for (int i=1;i<=cnt;i++)
    {
        ll x=Ext(chain[i]);
        if (Set.find(x)==Set.end())
            chain[++pnt]=chain[i],Set.insert(x);
    }
    cnt=pnt;
    return (ll)cnt*(1LL<<tot)-1;
}

#define V G[p].v
inline void dfs(int u,int fa){
    vst[u]=1; chain[++cnt]=depth[u];
    for (int p=head[u];p;p=G[p].next)
        if (V!=fa && !can[p])
        {
            if (!vst[V])
                depth[V]=depth[u]^G[p].w,dfs(V,u);
            else
                Ins(depth[V]^depth[u]^G[p].w);
        }
}

int Q,ed[M];
ll ans[M];

int main()
{
    int iu,iv; ll iw;
    freopen("t.in","r",stdin);
    freopen("t.out","w",stdout);
    read(n); read(m); read(Q);
    for (int i=1;i<=m;i++)
        read(iu),read(iv),read(iw),add(iu,iv,iw,++inum),add(iv,iu,iw,++inum);
    for (int i=1;i<=Q;i++) read(ed[i]),can[ed[i]<<1]=can[ed[i]<<1|1]=1;
//    for (int i=Q;i>=Q-10;i--) printf("%d %d %d\n",G[ed[i]<<1].u,G[ed[i]<<1].v,G[ed[i]<<1].w);
    dfs(1,0);
    ans[Q+1]=calc();
    for (int i=Q;i;i--)
    {
        iu=G[ed[i]<<1].u; iv=G[ed[i]<<1].v;
        if (vst[iu] && vst[iv]) Ins(depth[iv]^depth[iu]^G[ed[i]<<1].w);
        if (vst[iu] && !vst[iv]) depth[iv]=depth[iu]^G[ed[i]<<1].w,dfs(iv,iu);
        if (!vst[iu] && vst[iv]) depth[iu]=depth[iv]^G[ed[i]<<1].w,dfs(iu,iv);
        can[ed[i]<<1]=can[ed[i]<<1|1]=0;
        ans[i]=calc();
    }
    for (int i=1;i<=Q+1;i++)
        printf("%lld\n",ans[i]);
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值