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

题目:度度熊了解到,1,2,…,n 的排列一共有 n!=n×(n−1)×⋯×1 个。现在度度熊从所有排列中等概率随机选出一个排列 p1,p2,…,pn,你需要对 k=1,2,3,…,n 分别求出长度为 k 的上升子序列个数,也就是计算满足 1≤a1 < a2 < … < ak ≤n 且 pa1 <pa2< … < pak 的 k 元组 (a1,a2,…,ak) 的个数。    由于结果可能很大,同时也是为了 ruin the legend, 你只需要输出结果对 1000000007(=109+7) 取模后的值。

思路:因为是随机排列,循环长度用树状数组维护长度为len-1时的答案,滚动数组更新dp就行了。n*nlogn,随机序列能过的。。

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
const int maxn=1e4+10;
const ll mod=1e9+7;
int t,n,a[maxn];
ll dp[2][maxn],c[maxn],ans[maxn];
int lowbit(int x){return x&-x;}
void update(int pos,int val)
{
    while(pos<=n)
    {
        (c[pos]+=val)%=mod;
        pos+=lowbit(pos);
    }
}
ll query(int pos)
{
    ll ans=0;
    while(pos>0)
    {
        (ans+=c[pos])%=mod;
        pos-=lowbit(pos);
    }
    return ans;
}
int main()
{
    scanf("%d",&t);
    int cas=0;
    while(t--)
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            dp[1][i]=1;
        }
        memset(ans,0,sizeof ans);
        ans[1]=n;
        for(int len=2;len<=n;len++)
        {
            if(ans[len-1]==0) break;
            for(int i=1;i<=n;i++)
            {
                ll tmp=query(a[i]-1);
                (ans[len]+=tmp)%=mod;
                dp[len&1][i]=tmp;
                update(a[i],dp[(len-1)&1][i]);
            }
            memset(c,0,sizeof c);
        }
        printf("Case #%d:",++cas);
        for(int i=1;i<=n;i++)
            printf(" %lld",ans[i]);
        puts("");
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/dllpXFire/article/details/81451192