zoukankan      html  css  js  c++  java
  • bzoj3167 [Heoi2013]Sao

    传送门

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

    首先大家都知道原题等价于给每个点分配一个$1$~$n$且两两不同的权值,同时还需要满足一些大于/小于关系的方案数。

    先看一眼数据范围,既然写明了$nle 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
    ",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
  • 相关阅读:
    Codeforces Round #564 (Div. 1)
    Codeforces Round #569 (Div. 1)
    SDOI2019R2游记
    BZOJ 3555: [Ctsc2014]企鹅QQ
    SDOI2019R1游记
    计数的一些东西
    多项式的各种操作
    BZOJ 5424: 烧桥计划
    Codeforces Round #545 (Div. 1)
    概率期望学习笔记
  • 原文地址:https://www.cnblogs.com/hzoier/p/6999115.html
Copyright © 2011-2022 走看看