1. 程式人生 > >【BZOJ3167/4824】[Heoi2013]Sao/[Cqoi2017]老C的鍵盤

【BZOJ3167/4824】[Heoi2013]Sao/[Cqoi2017]老C的鍵盤

完全二叉樹 div 行為 orm sao tran input line ems

【BZOJ3167】[Heoi2013]Sao

Description

WelcometoSAO(StrangeandAbnormalOnline)。這是一個VRMMORPG,含有n個關卡。但是,挑戰不同關卡的順序是一個很大的問題。有n–1個對於挑戰關卡的限制,諸如第i個關卡必須在第j個關卡前挑戰,或者完成了第k個關卡才能挑戰第l個關卡。並且,如果不考慮限制的方向性,那麽在這n–1個限制的情況下,任何兩個關卡都存在某種程度的關聯性。即,我們不能把所有關卡分成兩個非空且不相交的子集,使得這兩個子集之間沒有任何限制。

Input

第一行,一個整數T,表示數據組數。對於每組數據,第一行一個整數n,表示關卡數。接下來n–1行,每行為“i sign j”,其中0≤i,j≤n–1且i≠j,sign為“<”或者“>”,表示第i個關卡必須在第j個關卡前/後完成。 T≤5,1≤n≤1000

Output

對於每個數據,輸出一行一個整數,為攻克關卡的順序方案個數,mod1,000,000,007輸出。

Sample Input

1
10
5 > 8
5 > 6
0 < 1
9 < 4
2 > 5
5 < 9
8 < 1
9 > 3
1 < 7

Sample Output

2580

題解:憋了一下午想出來的樹形DP題~

如何設狀態呢?顯然應該是個二維的狀態。設f[i][j]表示在i的子樹中,i位於第j個位置的方案數。那麽我們如何將x的當前狀態與他的兒子的狀態合並呢?

先只考慮x<y的情況,我們設原來的siz[x]=sa,siz[y]=sb,sa+sb=sc,我們想用f[x][a]和f[y][b]來更新f[x][c](c>=a+b),為了區分新的f和舊的f,我們用g[c]表示新的f。

如果x位於位置c,那麽它左邊有c-1個位置,相當於將y的序列中左邊的數與x的序列中左邊的數進行了二路歸並,並且歸並的順序可以隨便確定,那麽方案數就是$C_{c-1}^{a-1}$,同理,右面的sc-c個數也可以歸並處理,方案數是$C_{sc-c}^{sa-a}$,所以得到方程:

$g[c]=\sum\limits_{a=1}^{c}\sum\limits_{b=1}^{c-a}C_{c-1}^{a-1}C_{sc-c}^{sa-a}f[x][a]*f[y][b]$

可以將f[y][b]提出來

$g[c]=\sum\limits_{b=1}^{c}f[y][b]\sum\limits_{a=1}^{c-b}C_{c-1}^{a-1}C_{sc-c}^{sa-a}f[x][a]$

這個東西就可以用前綴和維護了~

不過值得慚愧的是,我的代碼的上界設的不緊,或是循環順序不太對,復雜度其實應該是O(n^3)的,似乎可以改一改使得復雜度變成樹形背包的優雅的O(n^2)。不過還是卡過了,就沒有改,求不卡~不過處理4824那題還是很輕松的,因為是完全二叉樹嘛~

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
typedef long long ll;
const ll P=1000000007;
int n,cnt;
ll ans;
ll C[1010][1010];
ll f[1010][1010],g[1010],s[1010];
int to[2010],next[2010],head[1010],val[2010],fa[1010],siz[1010];
char str[5];
void dfs(int x)
{
	int i,y,a,b,c;
	siz[x]=1,f[x][1]=1;
	for(i=head[x];i!=-1;i=next[i])	if(to[i]!=fa[x])
	{
		y=to[i],fa[y]=x,dfs(y);
		memset(g,0,sizeof(g[0])*(siz[x]+siz[y]+1));
		if(val[i]==1)
		{
			for(c=2;c<=siz[x]+siz[y];c++)
			{
				s[max(1,c-siz[y])-1]=0;
				for(b=1;b<=min(siz[x],c-1);b++)
					s[b]=(s[b-1]+C[c-1][b-1]*C[siz[x]+siz[y]-c][siz[x]-b]%P*f[x][b])%P;
				for(a=1;a<=min(siz[y],c-1);a++)
				{
					if(c-a>min(siz[x],c-1))	s[c-a]=s[min(siz[x],c-1)];
					if(c-a<max(1,c-siz[y]))	s[c-a]=0;
					g[c]=(g[c]+s[c-a]*f[y][a])%P;
				}
			}
			f[x][1]=0;
			for(c=2;c<=siz[x]+siz[y];c++)	f[x][c]=g[c];
		}
		else
		{
			for(c=2;c<=siz[x]+siz[y];c++)
			{
				s[max(1,c-siz[y])-1]=0;
				for(b=1;b<=min(siz[x],c-1);b++)
					s[b]=(s[b-1]+C[c-1][b-1]*C[siz[x]+siz[y]-c][siz[x]-b]%P*f[x][siz[x]-b+1])%P;
				for(a=1;a<=min(siz[y],c-1);a++)
				{
					if(c-a>min(siz[x],c-1))	s[c-a]=s[min(siz[x],c-1)];
					if(c-a<max(1,c-siz[y]))	s[c-a]=0;
					g[c]=(g[c]+s[c-a]*f[y][siz[y]-a+1])%P;
				}
			}
			f[x][siz[x]+siz[y]]=0;
			for(c=1;c<siz[x]+siz[y];c++)	f[x][c]=g[siz[x]+siz[y]-c+1];
		}
		siz[x]=siz[x]+siz[y];
	}
}
inline void add(int a,int b,int c)
{
	to[cnt]=b,val[cnt]=c,next[cnt]=head[a],head[a]=cnt++;
}
void work()
{
	scanf("%d",&n);
	int i,a,b;
	memset(head,-1,sizeof(head)),cnt=0;
	memset(f,0,sizeof(f)),memset(fa,0,sizeof(fa));
	for(i=1;i<n;i++)
	{
		scanf("%d%s%d",&a,str,&b),a++,b++;
		if(str[0]==‘>‘)	add(a,b,1),add(b,a,0);
		if(str[0]==‘<‘)	add(a,b,0),add(b,a,1);
	}
	dfs(1);
	ans=0;
	for(i=1;i<=n;i++)	ans=(ans+f[1][i])%P;
	printf("%lld\n",ans);
}
int main()
{
	int T,i,j;
	C[0][0]=1;
	for(i=1;i<=1000;i++)
	{
		C[i][0]=1;
		for(j=1;j<=i;j++)	C[i][j]=(C[i-1][j-1]+C[i-1][j])%P;
	}
	scanf("%d",&T);
	while(T--)	work();
	return 0;
}//5 10 5 > 8 5 > 6 0 < 1 9 < 4 2 > 5 5 < 9 8 < 1 9 > 3 1 < 7 10 6 > 7 2 > 0 9 < 0 5 > 9 7 > 0 0 > 3 7 < 8 1 < 2 0 < 4 10 2 < 0 1 > 4 0 > 5 9 < 0 9 > 3 1 < 2 4 > 6 9 < 8 7 > 1 10 0 > 9 5 > 6 3 > 6 8 < 7 8 > 4 0 > 6 8 > 5 8 < 2 1 > 8 10 8 < 3 8 < 4 1 > 3 1 < 9 3 < 7 2 < 8 5 > 2 5 < 6 0 < 9
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
typedef long long ll;
const ll P=1000000007;
int n,cnt;
ll ans;
ll C[1010][1010];
ll f[1010][1010],g[1010],s[1010];
int to[2010],next[2010],head[1010],val[2010],fa[1010],siz[1010];
char str[5];
void dfs(int x)
{
	int i,y,a,b,c;
	siz[x]=1,f[x][1]=1;
	for(i=head[x];i!=-1;i=next[i])	if(to[i]!=fa[x])
	{
		y=to[i],fa[y]=x,dfs(y);
		memset(g,0,sizeof(g[0])*(siz[x]+siz[y]+1));
		if(val[i]==1)
		{
			for(c=2;c<=siz[x]+siz[y];c++)
			{
				s[max(1,c-siz[y])-1]=0;
				for(b=1;b<=min(siz[x],c-1);b++)
					s[b]=(s[b-1]+C[c-1][b-1]*C[siz[x]+siz[y]-c][siz[x]-b]%P*f[x][b])%P;
				for(a=1;a<=min(siz[y],c-1);a++)
				{
					if(c-a>min(siz[x],c-1))	s[c-a]=s[min(siz[x],c-1)];
					if(c-a<max(1,c-siz[y]))	s[c-a]=0;
					g[c]=(g[c]+s[c-a]*f[y][a])%P;
				}
			}
			f[x][1]=0;
			for(c=2;c<=siz[x]+siz[y];c++)	f[x][c]=g[c];
		}
		else
		{
			for(c=2;c<=siz[x]+siz[y];c++)
			{
				s[max(1,c-siz[y])-1]=0;
				for(b=1;b<=min(siz[x],c-1);b++)
					s[b]=(s[b-1]+C[c-1][b-1]*C[siz[x]+siz[y]-c][siz[x]-b]%P*f[x][siz[x]-b+1])%P;
				for(a=1;a<=min(siz[y],c-1);a++)
				{
					if(c-a>min(siz[x],c-1))	s[c-a]=s[min(siz[x],c-1)];
					if(c-a<max(1,c-siz[y]))	s[c-a]=0;
					g[c]=(g[c]+s[c-a]*f[y][siz[y]-a+1])%P;
				}
			}
			f[x][siz[x]+siz[y]]=0;
			for(c=1;c<siz[x]+siz[y];c++)	f[x][c]=g[siz[x]+siz[y]-c+1];
		}
		siz[x]=siz[x]+siz[y];
	}
}
inline void add(int a,int b,int c)
{
	to[cnt]=b,val[cnt]=c,next[cnt]=head[a],head[a]=cnt++;
}
void work()
{
	scanf("%d",&n);
	int i,a,b;
	memset(head,-1,sizeof(head)),cnt=0;
	memset(f,0,sizeof(f)),memset(fa,0,sizeof(fa));
	for(i=1;i<n;i++)
	{
		scanf("%d%s%d",&a,str,&b),a++,b++;
		if(str[0]==‘>‘)	add(a,b,1),add(b,a,0);
		if(str[0]==‘<‘)	add(a,b,0),add(b,a,1);
	}
	dfs(1);
	ans=0;
	for(i=1;i<=n;i++)	ans=(ans+f[1][i])%P;
	printf("%lld\n",ans);
}
int main()
{
	int T,i,j;
	C[0][0]=1;
	for(i=1;i<=1000;i++)
	{
		C[i][0]=1;
		for(j=1;j<=i;j++)	C[i][j]=(C[i-1][j-1]+C[i-1][j])%P;
	}
	scanf("%d",&T);
	while(T--)	work();
	return 0;
}//5 10 5 > 8 5 > 6 0 < 1 9 < 4 2 > 5 5 < 9 8 < 1 9 > 3 1 < 7 10 6 > 7 2 > 0 9 < 0 5 > 9 7 > 0 0 > 3 7 < 8 1 < 2 0 < 4 10 2 < 0 1 > 4 0 > 5 9 < 0 9 > 3 1 < 2 4 > 6 9 < 8 7 > 1 10 0 > 9 5 > 6 3 > 6 8 < 7 8 > 4 0 > 6 8 > 5 8 < 2 1 > 8 10 8 < 3 8 < 4 1 > 3 1 < 9 3 < 7 2 < 8 5 > 2 5 < 6 0 < 9

【BZOJ3167/4824】[Heoi2013]Sao/[Cqoi2017]老C的鍵盤