bzoj3167 [Heoi2013]Sao
Posted AntiLeaf
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了bzoj3167 [Heoi2013]Sao相关的知识,希望对你有一定的参考价值。
这题神坑啊……明明是你菜
首先大家都知道原题等价于给每个点分配一个$1$~$n$且两两不同的权值,同时还需要满足一些大于/小于关系的方案数。
先看一眼数据范围,既然写明了$n\le 1000$,那就应该是什么$O(n^2)$的做法了。显然这个东西只能是个DP,考虑到题中给出的是一个树形结构,那么就可以利用子树的相对独立性进行DP:设$f_{i,j}$表示以$i$为根的子树中有$j$个点的权值大于$i$的权值时的方案数,显然最终答案就是$\sum_{i}f_{root,i}$。
然后考虑怎么求出答案。通常树形DP都是自底向上逐个合并来得到每个点的DP值的,但注意这个DP无法做到像普通的树形DP一样可以快速(比如$O(1)$或者O(log^2 n)之类)合并。不过这个DP还是可以比较高效地合并的,设要合并的两个子树大小分别为$n,m$,那么我们就可以通过枚举每一对$(i,j)$对应的$f_{\dots,i}$和$f_{\dots,j}$对合并后的DP数组的贡献来在$O(nm)$的时间内得到它们合并后的DP数组。
具体的合并过程就不写了,大体思路是先枚举最后$i$的权值在整个子树中的排名,然后枚举另一棵子树中有几个点权值比$i$的权值小,最后换元得到枚举$(i,j)$的形式。顺便一提,这个DP方程还需要一个前缀和优化才能做到$O(nm)$合并,还有大于和小于两种情况需要分开处理。
1 /************************************************************** 2 Problem: 3167 3 User: _Angel_ 4 Language: C++ 5 Result: Accepted 6 Time:3984 ms 7 Memory:8792 kb 8 ****************************************************************/ 9 #include<cstdio> 10 #include<cstring> 11 #include<algorithm> 12 #include<vector> 13 using namespace std; 14 const int maxn=1005,p=1000000007; 15 int C[maxn][maxn]; 16 struct DP{ 17 int f[maxn],n; 18 void clear(){ 19 memset(f,0,sizeof(f)); 20 n=1; 21 f[0]=1; 22 } 23 DP &operator+=(const DP &b){ 24 static int g[maxn]; 25 memset(g,0,sizeof(g)); 26 for(int i=0;i<n;i++)for(int j=1,tmp=0;j<=b.n;j++){ 27 tmp=(tmp+b.f[j-1])%p; 28 g[i+j]=(g[i+j]+(long long)f[i]*tmp%p*C[i+j][j]%p*C[n+b.n-i-j-1][b.n-j]%p)%p; 29 } 30 memcpy(f,g,sizeof(f)); 31 n+=b.n; 32 return *this; 33 } 34 DP &operator*=(const DP &b){ 35 static int g[maxn]; 36 memset(g,0,sizeof(g)); 37 int sum=0; 38 for(int j=0;j<b.n;j++)sum=(sum+b.f[j])%p; 39 for(int i=0;i<n;i++)for(int j=0,tmp=sum;j<b.n;j++){ 40 g[i+j]=(g[i+j]+(long long)f[i]*tmp%p*C[i+j][j]%p*C[n+b.n-i-j-1][b.n-j]%p)%p; 41 tmp=(tmp-b.f[j]+p)%p; 42 } 43 memcpy(f,g,sizeof(f)); 44 n+=b.n; 45 return *this; 46 } 47 }f[maxn]; 48 void dfs(int); 49 vector<int>G[maxn]; 50 vector<bool>W[maxn]; 51 int T,n,prt[maxn]; 52 int main(){ 53 C[0][0]=1; 54 for(int i=1;i<=1000;i++)for(int j=0;j<=i;j++){ 55 C[i][j]=C[i-1][j]; 56 if(j)C[i][j]=(C[i][j]+C[i-1][j-1])%p; 57 } 58 scanf("%d",&T); 59 while(T--){ 60 scanf("%d",&n); 61 memset(prt,0,sizeof(prt)); 62 for(int i=1;i<=n;i++){ 63 G[i].clear(); 64 W[i].clear(); 65 f[i].clear(); 66 } 67 for(int i=1,x,y;i<n;i++){ 68 char c; 69 scanf("%d %c%d",&x,&c,&y); 70 x++; 71 y++; 72 G[x].push_back(y); 73 W[x].push_back(c==‘>‘); 74 G[y].push_back(x); 75 W[y].push_back(c==‘<‘); 76 } 77 dfs(1); 78 int ans=0; 79 for(int i=0;i<n;i++)ans=(ans+f[1].f[i])%p; 80 printf("%d\n",ans); 81 } 82 return 0; 83 } 84 void dfs(int x){ 85 for(int i=0;i<(int)G[x].size();i++)if(G[x][i]!=prt[x]){ 86 prt[G[x][i]]=x; 87 dfs(G[x][i]); 88 if(W[x][i])f[x]+=f[G[x][i]]; 89 else f[x]*=f[G[x][i]]; 90 } 91 }
以上是关于bzoj3167 [Heoi2013]Sao的主要内容,如果未能解决你的问题,请参考以下文章
BZOJ3167/4824[Heoi2013]Sao/[Cqoi2017]老C的键盘