HDU-6348:序列计数(DP+树状数组)

序列计数
Time Limit: 4500/4000 MS (Java/Others)
Memory Limit: 262144/262144 K (Java/Others)

Problem Description
度度熊了解到,1,2,…,n 的排列一共有 n!=n×(n−1)×⋯×1 个。现在度度熊从所有排列中等概率随机选出一个排列 p 1 , p 2 , , p n ,你需要对 k=1,2,3,…,n 分别求出长度为 k 的上升子序列个数,也就是计算满足 1 a 1 < a 2 < < a k n p a 1 < p a 2 < < p a k 的 k 元组 ( a 1 , a 2 , , a k ) 的个数。

由于结果可能很大,同时也是为了 ruin the legend, 你只需要输出结果对 1000000007 ( = 10 9 + 7 ) 取模后的值。

Input
第一行包含一个整数 T,表示有 T 组测试数据。

接下来依次描述 T 组测试数据。对于每组测试数据:

第一行包含一个整数 n,表示排列的长度。

第二行包含 n 个整数 p1,p2, …, pn,表示排列的 n 个数。

保证 1 T 100 1 n 10 4 ,T 组测试数据的 n 之和 10 5 ,p1,p2,…,pn 是 1,2,…,n 的一个排列。

除了样例,你可以认为给定的排列是从所有 1,2,…,n 的排列中等概率随机选出的。

Output
对于每组测试数据,输出一行信息 “Case #x: c1 c2 … cn”(不含引号),其中 x 表示这是第 x 组测试数据,ci 表示长度为 i 的上升子序列个数对 1000000007 ( = 10 9 + 7 ) 取模后的值,相邻的两个数中间用一个空格隔开,行末不要有多余空格。

Sample Input
2
4
1 2 3 4
4
1 3 2 4

Sample Output
Case #1: 4 6 4 1
Case #2: 4 5 2 0

题解:记 d p i , j 为长度为 i 的以 p j 为结尾的上升子序列个数,那么有 d p i , j = k < j p k < p j d p i 1 , k
如果按照 j 从小到大的顺序递推,求和式中 k < j 这一项条件自然满足,只需要对所有满足 p k < p j 的 k 求 d p i 1 , k 的和,可以使用树状数组或者线段树维护,

由于输入的排列是随机选出的,一个有用的结论是最长上升子序列的长度期望是 O ( n )

#include<bits/stdc++.h>
using namespace std;
const int MAX=1e4+1;
const int MOD=1e9+7;
const double PI=acos(-1.0);
typedef long long ll;
inline int read()
{
    int f=1,x=0;char ch;
    do{ch=getchar();if(ch=='-')f=-1;}while(ch<'0'||ch>'9');
    do{x=x*10+ch-'0';ch=getchar();}while(ch>='0'&&ch<='9');
    return f*x;
}
int A[MAX],a[MAX];
void add(int x,int n,int y){while(x<=n){A[x]+=y;if(A[x]>=MOD)A[x]-=MOD;x+=x&(-x);}}
int ask(int x){int ans=0;while(x){ans+=A[x];if(ans>=MOD)ans-=MOD;x-=x&(-x);}return ans;}
int d[2][MAX];//滚动数组d[i]表示以a[i]为结尾的上升子序列个数
int ans[MAX];
int main()
{
    int T,cas=1;
    cin>>T;
    while(T--)
    {
        int n;
        scanf("%d",&n);
        for(int i=1;i<=n;i++)a[i]=read();
        for(int i=1;i<=n;i++)d[0][i]=1;
        for(int i=1;i<=n;i++)A[i]=ans[i]=0;
        ans[1]=n;
        int now=1;
        for(int i=2;i<=n;i++)//枚举上升子序列长度
        {
            if(ans[i-1]==0)break;
            for(int j=1;j<=n;j++)
            {
                int y=ask(a[j]-1)%MOD;
                (ans[i]+=y)%=MOD;
                add(a[j],n,d[now^1][j]);
                d[now][j]=y;
            }
            for(int j=1;j<=n;j++)A[j]=0;
            now^=1;
        }
        printf("Case #%d:",cas++);
        for(int i=1;i<=n;i++)printf(" %d",ans[i]);
        puts("");
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/Mitsuha_/article/details/81460153