codeforces-102501J Counting Trees题解

题意:给你一个二叉树的中序遍历$(n<=1000000)$,节点值为$1$~$1000000$的整数,可重复。问有多少种树的形态满足每个子树的根节点的权值小于等于该子树所有节点的权值。模$1e9+7$。

比较显然的一点是如果n个数各不相同,那么答案是1。每次我们找出当前区间的最小的数,把他作为根节点,然后将左右两个区间递归即可。

如果n个数都相同,那么答案就是第n个斯特林数。

斯特林数的递推式为$C_{n+1}=C_0C_n+C_1C_{n-1}+……+C_nC_0$

正好对应了枚举第几个数作为根节点的方案数。

经过仔细分析,假设当前区间为$(l,r)$,区间$(l,r)$的答案是$ans(l,r)$,最小值为$x$,$x$出现的位置为$x_1,x_2,x_3……,x_cnt$,可以发现,$ans(l,r)={ans}(l,x_1-1)*{ans}(x_1+1,x_2-1)*……*ans (x_{cnt-1}+1,x_{cnt}-1)*ans(x_cnt+1,r)*C_{cnt}$

所以,我们对区间1~n进行dfs即可。由于每个数最多会造成对两个区间的dfs,所以预处理斯特林数,构建st表以O(1)求区间最小值之后复杂度为$O(n)$。

具体实现方法为用$vector[x]$存储所有$x$出现的位置。由于我们的区间是从左到右进行dfs的,所以在计算第 $i$个$x$出现的位置时前$i-1$个$x$已经处理完了,我们设置一个$pos[x]$表示当前该处理到第几个$x$就可以了。

codeforces-102501J Counting Trees题解
  1 #include <bits/stdc++.h>
  2 #include<map>
  3 #define N 2000005
  4 using namespace std;
  5 int n,A[N/2],zz,B[N/2];
  6 const int p=1e9+7;
  7 map<int,int> ma;
  8 int st[N/2][22],xp[N/2];
  9 int pos[N/2];
 10 long long jc[N],ni[N];
 11 vector<int> q1[N/2];
 12 long long ksm(long long x,long long z)
 13 {
 14     long long ans=1;
 15     while(z)
 16     {
 17         if(z&1) ans=ans*x%p;
 18         x=x*x%p;    
 19         z>>=1;
 20     }
 21     return ans;
 22 }
 23 long long get_C(int x,int y)
 24 {
 25     if(y>x)return 0;
 26     return jc[x]*ni[y]%p*ni[x-y]%p;
 27 }
 28 long long get_E(int x)
 29 {
 30     return get_C(x*2,x)*ni[x+1]%p*jc[x]%p;
 31 }
 32 int get_mn(int L,int R)
 33 {
 34     int l=xp[R-L+1];
 35     return min(st[L][l],st[R-(1<<l)+1][l]);
 36 }
 37 long long dfs(int L,int R)
 38 {
 39     if(L>=R)return 1;
 40     int x=get_mn(L,R);
 41     long long ans=1;
 42     int len=q1[x].size();
 43     int cnt=0,La=L,nw=pos[x];
 44     while(nw<len&&q1[x][nw]<L) nw++;
 45     
 46     while(nw<len&&q1[x][nw]<=R)
 47     {
 48         cnt++;
 49         ans=ans*dfs(La,q1[x][nw]-1)%p;
 50         La=q1[x][nw]+1;
 51         nw++;
 52     }
 53     nw--;
 54     if(q1[x][nw]<=R&&q1[x][nw]>=L)
 55     {
 56         ans=ans*dfs(q1[x][nw]+1,R)%p;
 57     }
 58     pos[x]=nw+1;
 59     return ans*get_E(cnt)%p;
 60 }
 61 int main(){
 62     // freopen("test.in","r",stdin);
 63 //    freopen("1.out","w",stdout);
 64     scanf("%d",&n);
 65     if(!n)
 66     {
 67         printf("1\n");
 68         return 0;
 69     }
 70     jc[0]=1,ni[0]=1;
 71     for(int i=1;i<=n*2;i++) jc[i]=jc[i-1]*i%p;
 72     ni[n*2]=ksm(jc[n*2],p-2);
 73     for(int i=n*2-1;i;i--) ni[i]=ni[i+1]*(i+1)%p;
 74     for(int i=1;i<=n;i++)
 75     {
 76         scanf("%d",&A[i]);
 77         if(!ma[A[i]])
 78         {
 79             zz++;
 80             ma[A[i]]=zz;
 81             B[zz]=A[i];
 82         }
 83     }
 84     sort(B+1,B+zz+1);
 85     xp[1]=0;
 86     for(int i=2;i<=n;i++) xp[i]=xp[i>>1]+1;
 87     for(int i=1;i<=zz;i++) ma[B[i]]=i;
 88     for(int i=1;i<=n;i++) A[i]=ma[A[i]];
 89     for(int i=1;i<=n;i++) st[i][0]=A[i],q1[A[i]].push_back(i);
 90     for(int i=1;i<=20;i++)
 91     {
 92         for(int j=1;j<=n;j++)
 93         {
 94             if((j+(1<<i)-1)>n)break;
 95             st[j][i]=min(st[j][i-1],st[j+(1<<(i-1))][i-1]);
 96         }
 97     }
 98     printf("%lld\n",dfs(1,n));
 99     return 0;
100 }
101 /*
102 
103 10
104 1 2 3 1 3 2 3 1 2 2
105 6
106 3 1 6 2 4 5
107 */
View Code
上一篇:PAT 1004 Counting Leaves【n叉树层次遍历】


下一篇:一款代码扫描工具 火线!!!! fireline