【线性基】BZOJ2322 [BeiJing2011]梦想封印

【题目】
BZOJ
n n n个点 m m m条边的无向图,边有边权,有 Q Q Q次删边操作,每次操作后询问从 1 1 1号点出发有多少种不同的路径异或和,路径可以重复经过。
n , m , Q ≤ 2 × 10 n,m,Q\leq 2\times 10%^5,w_i\leq 10^{18} n,m,Q2×10
(原题数据更小一点qwq,实际上答案应该是 O ( W n ) O(Wn) O(Wn)级别的,不过可以出这么大就是了。)

【解题思路】
首先考虑 Q = 0 Q=0 Q=0的情况并假设图连通。
不妨求出图的一棵生成树,定义非树边对应的环为一条非树边和其端点在树上的路径之并。不难发现从 1 1 1开始任意路径的异或值等价于从 1 1 1开始的一条树链异或上一些非树边对应的环。

于是我们求出所有非树边对应环的线性基,对于每条从 1 1 1开始的树链权,我们在线性基上跑一遍,使得权值能变成 0 0 0的位全部变成 0 0 0。记录下有几个不同的结果(不妨使用 set \text{set} set),那么答案即再乘上 2 线 性 基 大 小 2^{线性基大小} 2线

对于 Q > 0 Q>0 Q>0,由于线性基(一般)不支持删除,不妨倒序将删除变为加入,此时分情况讨论。

  • 若加入边两个端点均不在 1 1 1连通块中,则对答案(暂时)无影响,先不管
  • 若加入边两个端点均在 1 1 1连通块中,则我们加入了一条非树边,那么我们需要在线性基中插入它对应的环的权值,然后将所有的从 1 1 1出发的树链再跑一次计算答案。由于线性基的性质,一共只会插入 log ⁡ W ) \log W) logW)次,显然是可行的。
  • 若加入边一个端点在 1 1 1连通块中,另一个不在,则我们需要计算新连通块带来的树链和非树边,我们仅需要从新增边开始将这个新连通块进行 DFS \text{DFS} DFS即可。然后再插入环即计算链。

复杂度 O ( n log ⁡ 2 W + ( m + Q ) α ( n ) ) O(n\log ^2 W+(m+Q)\alpha(n)) O(nlog2W+(m+Q)α(n))

我已经不会线性基了。

【参考代码】

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
const int N=2e5+10,M=63;
int tot,n,m,Q;
int head[N],fa[N],qr[N];
bool fg,ban[N],vis[N];
ll dis[N],fc[M];
vector<ll>ans,vec;
set<ll>st;

ll read()
{
	ll ret=0;char c=getchar();
	while(!isdigit(c)) c=getchar();
	while(isdigit(c)) ret=ret*10+(c^48),c=getchar();
	return ret;
}
void write(ll x){if(x>9)write(x/10);putchar(x%10^48);}
void writeln(ll x){write(x);putchar('\n');}

struct Linear_Basis
{
	ll A[M];int sz;//error because int A
	bool insert(ll x)
	{
		for(int i=M-1;~i;--i) if(x&fc[i])
		{
			if(!A[i]) {A[i]=x;++sz;return 1;}
			else x^=A[i];
		}
		return 0;
	}
	ll query(ll x)
	{
		for(int i=M-1;~i;--i) if((x&fc[i]) && A[i]) x^=A[i];
		return x;
	}
}B;	

struct Tway{ll w;int v,nex;}e[N<<1];
void add(int u,int v,ll w)
{
	e[++tot]=(Tway){w,v,head[u]};head[u]=tot;
	e[++tot]=(Tway){w,u,head[v]};head[v]=tot;
}

void dfs(int x)
{
	vis[x]=1;ll res=B.query(dis[x]);
	if(res) st.insert(res);
	for(int i=head[x];i;i=e[i].nex)
	{
		int v=e[i].v;
		if(v==fa[x] || ban[i>>1]) continue;
		if(!vis[v]) fa[v]=x,dis[v]=dis[x]^e[i].w,dfs(v);
		else if(B.insert(dis[v]^dis[x]^e[i].w)) fg=1; 
	}
}
void update()
{
	if(!fg) return;vec.clear();
	for(set<ll>::iterator it=st.begin();it!=st.end();++it) vec.push_back(*it);
	st.clear();ll x;//error because int x
	for(int i=0;i<(int)vec.size();++i) 
		if((x=B.query(vec[i]))) st.insert(x);
}
void rebuild(int k)
{
	int u=e[k<<1].v,v=e[k<<1|1].v;ll w=e[k<<1].w;
	ban[k]=fg=0;
	if(!vis[u] && !vis[v]) return;
	else if(!vis[u]) fa[u]=v,dis[u]=dis[v]^w,dfs(u);
	else if(!vis[v]) fa[v]=u,dis[v]=dis[u]^w,dfs(v);
	else if(B.insert(dis[v]^dis[u]^w)) fg=1;
	update();
}

int main()
{
#ifndef ONLINE_JUDGE
	freopen("BZOJ2322.in","r",stdin);
	freopen("BZOJ2322.out","w",stdout);
#endif
	fc[0]=1;for(int i=1;i<M;++i)fc[i]=fc[i-1]<<1;tot=1;
	n=read();m=read();Q=read();
	for(int i=1;i<=m;++i) 
	{
		int u=read(),v=read();ll w=read();
		add(u,v,w);
	}
	for(int i=1;i<=Q;++i) ban[qr[i]=read()]=1;

	ans.resize(Q+1);
	dfs(1);update();ans[Q]=1ll*(st.size()+1)*fc[B.sz]-1;
	for(int i=Q;i;--i) rebuild(qr[i]),ans[i-1]=1ll*(st.size()+1)*fc[B.sz]-1;
	for(int i=0;i<=Q;++i) writeln(ans[i]);

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值