码迷,mamicode.com
首页 > 其他好文 > 详细

bzoj3167 [Heoi2013]Sao

时间:2017-06-13 10:01:01      阅读:135      评论:0      收藏:0      [点我收藏+]

标签:logs   传送门   scanf   text   还需要   res   size   open   eof   

传送门

这题神坑啊……明明是你菜

首先大家都知道原题等价于给每个点分配一个$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 }
View Code

 

bzoj3167 [Heoi2013]Sao

标签:logs   传送门   scanf   text   还需要   res   size   open   eof   

原文地址:http://www.cnblogs.com/hzoier/p/6999115.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!