BZOJ2322: [BeiJing2011]梦想封印 线性基

题意:给一个无向图,边上有边权,每次删一条边,询问每次删边后从1号点出发任意走一条路径可能走出多少种不同的权值异或和。
N ≤ 5000,M ≤ 20000,Q ≤20000,权值1e18
先考虑静态版本。
每一条路径都可以看作由一条简单路径和若干个环组成。注意这里的环并不一定需要与路径相连,因为可以从起点绕环走一圈再走回起点,环以外的部分都被异或没了,相当于单独取了一个环。由此可以得到一个初步的思路:统计环的个数,统计路径的条数(包括不走,即只取环),用二者相乘。接下来考虑如何去重。
对于环来说,若一个环能被其他环异或出来,则这个环是重复的,因此维护线性基即可。这样线性基中不存在几个元素与另几个元素异或和相等的情况。证明:设异或和为x,则二者一定同时含有x最高位所对应的基底。去除这个元素递归这个过程,直到x变为0为止,可得出结论:二者的组成元素完全相同。证毕。
再考虑路径,我们的目的是使得不存在两条路径,使得它们各自异或上一些环后所得的异或值相等。因此我们用环的线性基对路径消元,从高位到低位,消不掉的部分就是这条路径中不能被线性基表示出的“特征值”,因此两条路径重复等价于其“特征值”相等,用set维护即可。
由此得到的线性基与路径集合已经满足任意组合都不会重复,因此size相乘减1(不能什么都不选)即为答案。
接下来考虑动态维护。由于可以离线,由删边变为加边,每加一条边,分情况讨论:
1.边的两侧都已经dfs过,说明找到了一个新环,把这个环的权值算出加入线性基中。
2.边的一侧dfs过,那么可以沿着没dfs过的那侧开始新的dfs。
3.两侧都没dfs过,说明对于当前的图是没有影响的,不用管了,留着以后用。
注意:一旦线性基发生变化,说明路径的“特征值”也会改变,因此将路径集合中的所有路径用新产生的那个基底进行消元后重建集合。

#ifdef ONLINE_JUDGE
#define null_type null_mapped_type
#define NDEBUG
#undef _DEBUG
#endif
#include<cstdio>
#include<ext/pb_ds/assoc_container.hpp>
using namespace __gnu_pbds;
typedef unsigned long long ll;
struct set
{
    typedef cc_hash_table<ll,null_type> base_set;
    base_set s;
    set():s(){s.insert(0);}
    inline void push(const ll &x)
    {
        s.insert(x);
    }
    inline size_t size()
    {
        return s.size();
    }
    inline void update(const ll &x)
    {
        static ll val[5000];
        int top=-1;
        for(base_set::iterator i=s.begin(),j=s.end();i!=j;++i)
        {
            val[++top]=*i;
            if((val[top]^x)<val[top]) val[top]^=x;
        }
        s=base_set(val,val+top+1);
    }
}s;
struct gay
{
    ll base[64];
    size_t sz;
    gay():base(),sz(){}
    inline void push(ll x)
    {
        for(int i=63;~i;--i)
        if(x>>i&1) x^=base[i];
        if(!x) return;
        s.update(x);
        ++sz;
        for(int i=63;~i;--i)
        if(x>>i&1) {base[i]=x;break;}
    }
    inline size_t size()
    {
        return sz;
    }
    inline ll cosmos(ll x)
    {
        for(int i=63;~i;--i)
        if(x>>i&1) x^=base[i];
        return x;
    }
}b;
struct graph
{
    ll v[5001];
    bool used[5001];
    struct e
    {
        int t;
        ll c;
        e *n;
        e(int t,e *n,const ll& c):t(t),n(n),c(c){}
    }*f[5001];
    graph():v(),used(),f(){used[1]=1;}
    void dfs(int x,int from)
    {
        used[x]=1;
        s.push(b.cosmos(v[x]));
        int y;
        for(e *i=f[x];i;i=i->n)
        {
            y=i->t;
            if(y==from) continue;
            if(!used[y]) v[y]=v[x]^i->c,dfs(y,x);
            else b.push(v[x]^v[y]^i->c);
        }
    }
    inline void push(int x,int y,const ll&c)
    {
        f[x]=new e(y,f[x],c);
        f[y]=new e(x,f[y],c);
        if(used[x]&&used[y]) b.push(v[x]^v[y]^c);
        else if(used[x]) v[y]=v[x]^c,dfs(y,x);
        else if(used[y]) v[x]=v[y]^c,dfs(x,y);
    }
}g;
int n,m,q;
int x[20000],y[20000];
ll w[20000];
int dis[20000];
bool del[20000];
ll ans[20001];
inline ll calc()
{
    return (1ull<<b.size())*s.size()-1;
}
int main()
{
    scanf("%d%d%d",&n,&m,&q);
    for(int i=0;i<m;++i) scanf("%d%d%llu",x+i,y+i,w+i);
    for(int i=0;i<q;++i) scanf("%d",dis+i),--dis[i],del[dis[i]]=1;
    for(int i=0;i<m;++i) if(!del[i]) g.push(x[i],y[i],w[i]);
    ans[q]=calc();
    for(int i=q-1;~i;--i)
    {
        int j=dis[i];
        g.push(x[j],y[j],w[j]);
        ans[i]=calc();
    }
    for(int i=0;i<=q;++i)
    printf("%llu\n",ans[i]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值