1. 程式人生 > >【BZOJ2322】[BeiJing2011]夢想封印 高斯消元求線性基+DFS+set

【BZOJ2322】[BeiJing2011]夢想封印 高斯消元求線性基+DFS+set

思考 包含 cst 計算 next 裏的 是否 異或和 無法使用

【BZOJ2322】[BeiJing2011]夢想封印

Description

漸漸地,Magic Land上的人們對那座島嶼上的各種現象有了深入的了解。

為了分析一種奇特的稱為夢想封印(Fantasy Seal)的特技,需要引入如下的概念:

每一位魔法的使用者都有一個“魔法脈絡”,它決定了可以使用的魔法的種類。

一般地,一個“魔法脈絡”可以看作一個無向圖,有N個結點及M條邊,將結點編號為1~N,其中有一個結點是特殊的,稱為核心(Kernel),記作1號結點。每一條邊有一個固有(即生成之後再也不會發生變化的)權值,是一個不超過U的自然數。

每一次魔法驅動,可看作是由核心(Kernel)出發的一條有限長的道路(Walk),可以經過一條邊多次,所驅動的魔法類型由以下方式給出:

將經過的每一條邊的權值異或(xor)起來,得到s。

如果s是0,則驅動失敗,否則將驅動編號為s的魔法(每一個正整數編號對應了唯一一個魔法)。

需要註意的是,如果經過了一條邊多次,則每一次都要計入s中。

這樣,魔法脈絡決定了可使用魔法的類型,當然,由於魔法與其編號之間的關系尚未得到很好的認知,此時人們僅僅關註可使用魔法的種類數。

夢想封印可以看作是對“魔法脈絡”的破壞:

該特技作用的結果是,“魔法脈絡”中的一些邊逐次地消失。

我們記總共消失了Q條邊,按順序依次為Dis1、Dis2、……、DisQ。

給定了以上信息,你要計算的是夢想封印作用過程中的效果,這可以用Q+1個自然數來描述:

Ans0為初始時可以使用魔法的數量。

Ans1為Dis1被破壞(即邊被刪去)後可以使用魔法的數量。

Ans2為Dis1及Dis2均被破壞後可使用魔法的數量。

……

AnsQ為Dis1、Dis2、……、DisQ全部被破壞後可以使用魔法的數量。

Input

第一行包含三個正整數N、MQ

接下來的M行,每行包含3個整數,Ai、Bi、Wi,表示一條權為Wi的與結點Ai、Bi關聯的無向邊,其中Wi是不超過U的自然數。

接下來Q行,每行一個整數:Disi。

Output

一共包Q+1行,依次為Ans0、Ans1、……、AnsQ。

Sample Input

【輸入樣例1】
3 3 2
1 2 1
2 3 2
3 1 4
1
3
【輸入樣例2】
5 7 7
1 2 1
1 3 1
2 4 2
2 5 2
4 5 4
5 3 9
4 3 1
7
6
5
4
3
2
1

Sample Output

【輸出樣例1】
5
2
0
【樣例1解釋】
初始時可使用編號為1、3、4、6、7的魔法。
在刪去第1條邊(連結1、2結點的邊)後,可使用4和6號魔法。
第3條邊(連結第1、3結點的邊)也被刪去後,核心(Kernel)即結點1孤立,易知此時無法使用魔法。
【輸出樣例2】
15
11
5
2
2
1
1
0

HINT

【數據規模和約定】
所有數據保證該無向圖不含重邊、自環。

所有數據保證不會有一條邊被刪除多次,即對於不同i和j,有Disi≠Disj

30%的數據中N ≤ 50,M ≤ 50,Q ≤50,U≤100;

60%的數據中N ≤ 300,M ≤ 300,Q ≤50,U≤10^9;

80%的數據中N ≤ 300,M ≤ 5000,Q ≤5000,U≤10^18;

100%的數據中N ≤ 5000,M ≤ 20000,Q ≤20000,U≤10^18;

題解:又一道神題

我們回憶2155那道題的做法:所有從1到一個點的路徑的異或和 都可以表示成 任意一條從1到該節點的路徑 和 某些簡單環 的異或和。

那麽本題變成了動態求異或和的種類數,我們首先的思路就是將刪邊變成倒著往圖中加邊。然後思考一下我們具體都需要維護些什麽。

從答案的角度分析,答案=(所有本質不同的路徑數)*2^(本質不同的環的個數)-1,註意本質不同的路徑包括哪裏都不走,所以最後要-1。具體本質不同是什麽意思呢?

回憶2155那道題,本質不同的環的意思就是:將所有環高斯消元後得到的線性基,因為這樣就可以表示任意環的異或和。

那麽本質不同的路徑的意思也就出來了,我們最終得到的不簡單的路徑的異或和=任意一條簡單路徑的異或和^任意一些環的異或和,所以兩條簡單路徑本質不同當且僅當它們用線性基消元後,得到的值不同。具體地,我們可以用set來維護這樣的路徑。

好了,分析了這麽多,現在我們終於可以得出一個可行的做法了:當加入邊(a,b)時:

1.若a,b都已經被訪問過(或者說加入的是一條非樹邊),我們此時得到了一個簡單環,將這個簡單環的異或和放到線性基中消元,如果沒有消成0,那就說明我們應該將這個圓加入到線性基中去,直接把它加入到線性基中對應的位置就行了。但是註意一點,更改線性基後,之前的路徑並沒有被這條邊消元,所以要把set裏的邊一個一個取出來,重新消元再塞回去。

2.若a被訪問過b沒有被訪問過(或者說加入了一條樹邊),我們應該DFSb所在的連通塊。在DFS的過程中,每搜到一個點,我們就計算出從1到這個點的路徑的異或和,消元後看一下跟以前的是否重復,如果不重復就扔到set裏。此外,每搜到一條樹邊,我們就繼續向下搜索;每搜到一條非樹邊,我們就又得到了一個簡單環,按照1中的處理方法去處理就行了。

3.若a,b都沒有被訪問過(我們也不知道它將會是樹邊還是非樹邊),不用管就好。

感覺實現要比理論簡單一些。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <set>
#include <algorithm>
typedef long long ll;
using namespace std;
int n,m,cnt,q;
int to[40010],next[40010],head[10010],pa[40010],pb[40010],del[40010],tag[40010],vis[10010];
ll val[40010],dis[10010],pc[40010],ans[40010];
ll v[110];
set<ll> s;
set<ll>::iterator it;
bool cmp(ll a,ll b)
{
	return a>b;
}
ll query(ll x)
{
	for(int j=1;j<=v[0];j++)	if((x^v[j])<x)	x^=v[j];
	return x;
}
void updata(ll x)
{
	if(!x)	return ;
	ll tmp;
	for(it=s.begin();it!=s.end();it=s.upper_bound(tmp))
	{
		tmp=*it;
		if((tmp^x)<tmp)	s.erase(it),s.insert(tmp^x);
	}
	v[++v[0]]=x;
	for(int j=v[0];j>=2;j--)
	{
		if(v[j]>v[j-1])	swap(v[j],v[j-1]);
		else break;
	}
}
void add(int a,int b,ll c)
{
	to[cnt]=b,val[cnt]=c,next[cnt]=head[a],head[a]=cnt++;
}
void dfs(int x,int fa)
{
	vis[x]=1;
	ll tmp=query(dis[x]);
	if(tmp&&s.find(tmp)==s.end())	s.insert(tmp);
	for(int i=head[x];i!=-1;i=next[i])
	{
		if(to[i]==fa)	continue;
		if(!vis[to[i]])	dis[to[i]]=dis[x]^val[i],dfs(to[i],x);
		else	updata(query(dis[x]^dis[to[i]]^val[i]));
	}
}
void insert(int a,int b,ll c)
{
	add(a,b,c),add(b,a,c);
	if(vis[a]&&vis[b])
	{
		updata(query(dis[a]^dis[b]^c));
		return ;
	}
	if(!vis[a]&&!vis[b])	return ;
	if(vis[b])	swap(a,b);
	dis[b]=dis[a]^c,dfs(b,a);
}
int main()
{
	memset(head,-1,sizeof(head));
	vis[1]=1;
	scanf("%d%d%d",&n,&m,&q);
	int i;
	for(i=1;i<=m;i++)	scanf("%d%d%lld",&pa[i],&pb[i],&pc[i]);
	for(i=1;i<=q;i++)	scanf("%d",&del[i]),tag[del[i]]=1;
	for(i=1;i<=m;i++)	if(!tag[i])	insert(pa[i],pb[i],pc[i]);
	s.insert(0);
	ans[q+1]=(s.size()*1ll<<v[0])-1;
	for(i=q;i>=1;i--)
	{
		insert(pa[del[i]],pb[del[i]],pc[del[i]]);
		ans[i]=(s.size()*(1ll<<v[0]))-1;
	}
	for(i=1;i<=q+1;i++)	printf("%lld\n",ans[i]);
	return 0;
}

【BZOJ2322】[BeiJing2011]夢想封印 高斯消元求線性基+DFS+set