JZOJ 3317. 【BOI2013】管道(環套樹+解方程)

JZOJ 3317. 【BOI2013】管道

題目

Description

Hotham市再次被Jester襲擊。這一次,Jester攻擊的目標是Hotham的供水系統。Hotham的淡水存儲在N個水庫,由M個管道連接。

任意2個水庫之間至少有一條路徑,該路徑可能包含多個管道。每個管道連接兩個不同的水庫,任何兩個水庫之間最多隻有一個管道。Jester破壞了一些管道進行排水。管道的排水量爲偶數立方米/秒。如果連接水庫u和水庫v的管道排水量爲2D立方米/秒,那麼水庫u和水庫v排水量分別爲D立方米/秒。Jester可以也破壞一些管道進行注水。同樣,連接水庫u和v的管道注入水量爲2P立方米/秒,則水庫u和水庫v注水量分別爲P立方米/秒。

每個水庫供水量的淨變化是由連接到它的管道的排水量和注水量的總和。如果連接該水庫的管道排水量分別爲2D1, 2D2,…,2Da立方米/秒, 注水量分別爲2P1, 2P2 ,…,2Pb 立方米/秒。該水庫水量的淨變化爲P1 + P2 + … + Pb -D1 - D2 –Da。

Hotham市長已在水庫安裝了傳感器,但不是在管道中。因此,他可以觀察到在每個水庫水的淨變化,但並知道有多少水被排出或注入各個管道。

給出水管連接圖和每個水庫供水量的淨變化,編寫程序決定這些信息是否能確定一個唯一的計劃。如果各個水管排出或注入的水量是唯一的,那麼該計劃可以被唯一確定。請注意,並不是每一個水管中的注水量和排水量都相等。如果恰好有一個可能性,你的程序應該打印出來。

Input

輸入的第一行包含兩個整數:N:Hotham的水庫數 和M:管道數。

接下來的N行每行包含一個整數ci:表示水庫i(1≤i≤N)的水量淨變化。

再接下來的M行每行包含兩個整數ui和vi(1≤i≤M)。每一行分別表示,水庫 ui和vi之間有管道連接(1≤ui,vi≤N)。

Output

如果Jester的計劃不能被唯一確定,你的程序則輸出0。否則,你的程序應該輸出M行,每一行包含一個整數Xi(1≤i≤M)。 如果Jester從連接水庫ui和vi間的管道里排水,該管道的排水量爲di立方米/秒,則設Xi=-di。如果Jester往連接水庫ui和vi間的管道里注水,該管道的注水量爲pi立方米/秒,則Xi=pi。如果Jester對連接水庫ui和vi間的管道既不排水也不注水,則Xi =0。

Sample Input

輸入1:
4 3
-1
1
-3
1
1 2
1 3
1 4

輸入2:
4 5
1
2
1
2
1 2
2 3
3 4
4 1
1 3

Sample Output

輸出1:
2
-6
2

輸出2:
0

Data Constraint

1N1000001≤N≤100 000
1M5000001≤M≤500 000
109Ci109-10^9≤C_i≤10^9
如果方案唯一,則109Xi109-10^9≤Xi≤10^9
30%的數據滿足供水系統的網絡是一棵樹。

題解

  • (題目最早竟然沒看懂,什麼什麼又是D的又是P的,後來發現這是同一個東西)
  • 很顯然可以把每條邊看作一個未知數,把每個點看作一條方程,
  • 那麼它們可以組成一個方程組……
  • And then?
  • 一秒想到高斯消元,
  • 一秒看到數據,知道這不可能。。。
  • 所以怎麼做?
  • 把所有的CiC_i22
  • 先分類討論:
  • 一、m>nm>n
  • 未知數個數大於方程組個數,沒有唯一解,Ans=0Ans=0
  • 二、m=n1m=n-1
  • 這就是一棵樹,非常好求解(可能無解),
  • 從葉子節點向上,每次可以知道當前點xxCxC_x,與它相連的只有上面的一條邊ee
  • 那麼自然可以得出ee的解s[e]=Cxs[e]=C_x,再對應的CfaiC_{fa_i}減去s[e]s[e]
  • 這樣下去 一直到根,判斷Croot=0C_{root}=0那麼有唯一解,否則無解。
  • 三、m=nm=n
  • 一個優美的nn元一次方程組,但也不能用解方程組的常規辦法
  • 首先可以知道,這是個環套樹(只有一個環),
  • 那麼隨便亂搜把這個環和對應的邊找出來,
  • 如果是偶環,自己隨便推一推即可知道要麼無解,要麼有無窮多解,所以不成立;
  • 如果是奇環,
  • 以環中每個點爲根分別進行情況二中的步驟,
  • 但與之不同的是,不需判斷任何東西,每個點的CC剩多少就是多少,
  • 之後就只剩一個單純的環了,
  • 可以發現有方程組是這樣的:(mm是環中的點數)
  • x1+x2=C1x_1+x_2=C_1
  • x2+x3=C2x_2+x_3=C_2
  • ……
  • xm+x1=Cmx_m+x_1=C_m
  • 按照小學奧數題的辦法,
  • 先計算出sum=i=1mxisum=\sum_{i=1}^mx_i,它實際就等於i=1mCi2\frac{\sum_{i=1}^mC_i}{2}
  • 接着舉個例子,
  • 如果ii是奇數,設i=3i=3m=7m=7
  • s=sumx1x2x4x5x6x7=sumC1C4C6s=sum-x_1-x_2-x_4-x_5-x_6-x_7=sum-C_1-C_4-C_6
  • 如果ii是偶數,設i=4i=4m7m-7
  • s=sumx2x3x5x6x7x1=sumC2C5C7s=sum-x_2-x_3-x_5-x_6-x_7-x_1=sum-C_2-C_5-C_7
  • 分奇偶性各維護一個CC的前綴和,再用sumsum減去前面一段和後面一段。

代碼

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 100010
int a[N],to[N*2],d[N*2],next[N*2],last[N],len=0;
int bz[N],t[N],g[N],c[N],s[N],st[N],sg[N],ss=0;
struct
{
	int x,y;
}b[N];
void add(int x,int y,int id)
{
	to[++len]=y,d[len]=id;
	next[len]=last[x];
	last[x]=len;
}
int dfs(int x,int fa)
{
	int s=a[x];
	for(int i=last[x];i;i=next[i]) if(to[i]!=fa&&!bz[to[i]])
	{
		c[d[i]]=dfs(to[i],x);
		s-=c[d[i]];
	}
	return s;
}
void dg(int x,int fa)
{
	bz[x]=1;
	t[++t[0]]=x;
	for(int i=last[x];i;i=next[i]) if(to[i]!=fa)
	{
		if(!bz[0]&&bz[to[i]]) 
		{
			bz[0]=1;
			g[t[0]]=d[i];
			int ok=0;
			for(int j=1;j<=t[0];j++)
			{
				if(t[j]==to[i]) ok=1;
				if(ok)
				{
					st[++ss]=t[j];
					sg[ss]=g[j];	
				}
			}
			return;
		}
		if(!bz[0]) g[t[0]]=d[i],dg(to[i],x);
	}
	if(bz[0]) return;
	t[0]--;
}
int main()
{
	int n,m,i,x,y;
	scanf("%d%d",&n,&m);
	if(m>n)
	{
		printf("0");
		return 0;
	}
	for(i=1;i<=n;i++) scanf("%d",&a[i]),a[i]*=2;
	for(i=1;i<=m;i++) scanf("%d%d",&x,&y),add(x,y,i),add(y,x,i);
	if(m==n-1)
	{
		if(dfs(1,0)!=0) printf("0");
		else for(i=1;i<=m;i++) printf("%d\n",c[i]);
		return 0;
	}
	dg(1,0);
	if(ss%2==0)
	{
		printf("0");
		return 0;
	}
	memset(bz,0,sizeof(bz));
	for(i=1;i<=ss;i++) bz[st[i]]=1;
	int sum=0;
	for(i=1;i<=ss;i++)
	{
		a[st[i]]=dfs(st[i],0);
		if(i==1) s[i]=a[st[i]]; else s[i]=s[i-2]+a[st[i]];
		sum+=a[st[i]];
	}
	sum/=2;
	for(i=1;i<=ss;i++)
		if(i%2==1) c[sg[i]]=sum-s[ss]+s[i]-s[i-1]; else c[sg[i]]=sum-s[ss-1]+s[i]-s[i-1];
	for(i=1;i<=m;i++) printf("%d\n",c[i]);
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章