【XIV Open Cup E.V. Pankratiev. GP of SPb. H】Reachability 題解

題目大意

  一幅有向圖有 nn 個結點,初始沒有邊。
  有 qq 個操作,四種類型:

  • + o v k a1  ak+~o~v~k~a_1~\cdots~a_k:加入邊 (v,a1),,(v,ak)(v,a_1),\cdots,(v,a_k)
  • + i v k a1  ak+~i~v~k~a_1~\cdots~a_k:加入邊 (a1,v),,(ak,v)(a_1,v),\cdots,(a_k,v)
  •  o v k a1  ak-~o~v~k~a_1~\cdots~a_k:刪除邊 (v,a1),,(v,ak)(v,a_1),\cdots,(v,a_k)
  •  i v k a1  ak-~i~v~k~a_1~\cdots~a_k:刪除邊 (a1,v),,(ak,v)(a_1,v),\cdots,(a_k,v)

  加邊之前會保證原來沒有這條邊,刪邊之前會保證原來有這條邊。
  每次操作後,可以得到一個連通性矩陣 aaai,j=1a_{i,j}=1 表示 ii 能到 jj),輸出
(i,j=1nai,jAi1Bj1) mod 232\bigg(\sum_{i,j=1}^n a_{i,j}A^{i-1}B^{j-1}\bigg)~\text{mod}~2^{32}
  1n400, 1q800, 1A,B1091 \leq n \leq 400,~1 \leq q \leq 800,~1 \leq A,B \leq 10^9
  3s

\\
\\
\\

解法1

  每個點維護一個 bitset 表示它能到哪些點。
  每次操作時,暴力重構 vv 的 bitset,然後 bfs 一下把能到 vv 的點找出來,按拓撲序更新它們的 bitset。維護 bitset 時順便維護答案。
  對於加邊操作的更新,是 bitseti=bitsetibitsetvbitset_i = bitset_i \vee bitset_v。這個的時間主要在於圖的遍歷,邊表是 O(n2)O(n^2) 的,因此時間是 O(q(n2+nn64))=O(qn2)O\big(q(n^2+n\frac n{64})\big)=O(qn^2)
  對於刪邊操作的更新,是 bitseti=xout(i)bitsetxbitset_i = \bigvee_{x \in \text{out}(i)}bitset_xout(i)\text{out}(i) 表示 ii 的出點),由於之前是或操作不可撤銷,因此這個要對於 ii 枚舉它的出點重新算,因此是 O(q(n2+n2n64))=O(qn364)O\big(q(n^2+n^2\frac n{64})\big)=O(\frac{qn^3}{64})

  因此總的時間是 O(qn364)O(\frac{qn^3}{64}),算出來 8 億但是跑過去了。

解法2

  上面的解法,加邊很優秀,但是刪邊不太行,這是因爲刪邊的時候由於維護的是“是否連通”,所以無法快速撤銷一個出點的影響。

  那什麼可以撤銷呢?方案數就可以撤銷!

  記 fi,jf_{i,j} 表示 ii 走到 jj 的方案數,給它模個 109+710^9+7 啥的(不放心就多模幾個)。
  每次操作時,暴力重算 fv,f_{v,\cdot} 或者 f,vf_{\cdot,v},然後對於所有 (i,j)(i,j),先 fi,j=f_{i,j}-=原來的fi,v+fv,jf_{i,v}+f_{v,j},再 +=+=新的fi,v+fv,jf_{i,v}+f_{v,j}

  這樣就是 O(qn2)O(qn^2) 的了。

代碼

// 解法1

#include<bits/stdc++.h>
#define fo(i,a,b) for(int i=a;i<=b;i++)
using namespace std;

typedef unsigned int uint;

const int maxn=405;

int n,q,len,x[maxn];
uint A[maxn],B[maxn];
bool mp[maxn][maxn];
bitset<maxn> a[maxn];

void ReadChar(char &ch)
{
	ch=getchar();
	while (ch!='+' && ch!='-' && ch!='o' && ch!='i') ch=getchar();
}

bool bz[maxn];
void dfs1(int k,int v)
{
	if (v!=k) a[v][k]=1;
	bz[k]=1;
	fo(i,1,n) if (mp[k][i] && !bz[i]) dfs1(i,v);
}

int d[3*maxn],dg[maxn];
void topo(int v,char ty)
{
	fo(i,1,n) bz[i]=0, dg[i]=0;
	int j=0;
	if (ty=='o') d[j=1]=v;
		else fo(i,1,len) d[++j]=x[i];
	for(int i=1; i<=j; i++)
	{
		fo(go,1,n) if (mp[go][d[i]])
		{
			dg[go]++;
			if (!bz[go]) bz[ d[++j]=go ]=1;
		}
	}
}

uint ans,Ans[maxn];
void Redo(int v)
{
	ans-=Ans[v];
	Ans[v]=0;
	fo(i,1,n) if (a[v][i]) Ans[v]+=A[v-1]*B[i-1];
	ans+=Ans[v];
}

int main()
{
	freopen("reachability.in","r",stdin);
	freopen("reachability.out","w",stdout);
	
	scanf("%d %d %u %u",&n,&q,&A[1],&B[1]);
	A[0]=B[0]=1;
	fo(i,2,n) A[i]=A[i-1]*A[1], B[i]=B[i-1]*B[1];
	while (q--)
	{
		char ch1,ch2; int v;
		ReadChar(ch1), ReadChar(ch2);
		scanf("%d %d",&v,&len);
		fo(i,1,len)
		{
			scanf("%d",&x[i]);
			if (ch2=='o') mp[v][x[i]]^=1; else mp[x[i]][v]^=1;
		}
		
		if (ch2=='o')
		{
			a[v].reset();
			fo(i,1,n) bz[i]=0;
			dfs1(v,v);
			Redo(v);
		}
		
		topo(v,ch2);
		if (ch1=='+')
		{
			int j=0;
			if (ch2=='o') d[j=1]=v;
				else fo(i,1,len) d[++j]=x[i];
			for(int i=1; i<=j; i++)
			{
				a[d[i]]|=a[v];
				if (d[i]!=v) a[d[i]][v]=1;
				Redo(d[i]);
				fo(go,1,n) if (mp[go][d[i]])
				{
					if (--dg[go]==0) d[++j]=go;
				}
			}
		} else
		{
			int j=0;
			if (ch2=='o') d[j=1]=v;
				else fo(i,1,len) d[++j]=x[i];
			for(int i=1; i<=j; i++)
			{
				a[d[i]].reset();
				fo(go,1,n) if (mp[d[i]][go]) a[d[i]]|=a[go], a[d[i]][go]=1;
				Redo(d[i]);
				fo(go,1,n) if (mp[go][d[i]])
				{
					if (--dg[go]==0) d[++j]=go;
				}
			}
		}
		
		printf("%u\n",ans);
	}
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章