【BZOJ3167/4824】[Heoi2013]Sao/[Cqoi2017]老C的鍵盤
【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≤1000Output
對於每個數據,輸出一行一個整數,為攻克關卡的順序方案個數,mod1,000,000,007輸出。
Sample Input
110
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的鍵盤