2018百度之星资格赛补题

hdu6348

序列计数

Time Limit: 4500/4000 MS (Java/Others)    Memory Limit: 262144/262144 K (Java/Others)
Total Submission(s): 253    Accepted Submission(s): 75

Problem Description

度度熊了解到,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) 取模后的值。

Input

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

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

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

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

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

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

Output

对于每组测试数据,输出一行信息 "Case #x: c1 c2 ... cn"(不含引号),其中 x 表示这是第 x 组测试数据,ci 表示长度为 i 的上升子序列个数对 1000000007(=109+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

题意:给定一个打乱的包含1~n数字的序列,分别求上升序列长度为1~n个序列个数。

思路:由长度小的求长度大的,如通过知道以x结尾的长度为2的上升序列个数可以知道在x后面且比x大的y的长度为3的上升序列个数(一部分,再将所有满足的x求和即可)。树状数组维护。由于输入的排列是随机选出的,一个有用的结论是最长上升子序列的长度期望是 O(sqrt(n))。

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn = 1e4+10;
const ll mod = 1e9+7;
int a[maxn];  //每个位置的值
ll c[maxn], num[maxn];   //c为树状数组,num[i]为当前长度以i为结尾的序列数
int n;  
int Lowbit(int x)  // 2^k  Lowbit(x)为x保留最右边1的结果
{  
    return x&(-x);  
}  
void update(int i, ll x)//i点增量为x ,更新树状数组 
{  
    while(i <= n)  
    {  
        c[i] += x;  
        i += Lowbit(i);  
    }  
}  
ll sum(int x)//区间求和 [1,x]  
{  
    int sum=0;  
    while(x>0)  
    {  
        sum=(sum + c[x])%mod;  
        x-=Lowbit(x);  
    }  
    return sum;  
}  
int main()
{
    int t;
    scanf("%d",&t);
    for(int tt = 1;tt<=t;tt++)
    {
        scanf("%d",&n);
        for(int i = 1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            num[i] = 1;
        }
        int len = n;
        ll res = n;
        printf("Case #%d:",tt);
        while(res)
        {
            printf(" %lld",res);
            len--;
            memset(c,0,sizeof(c));
            res = 0;
            for(int i=1;i<=n;i++)
            {
                update(a[i],num[i]);
                num[i] = sum(a[i]-1);  //上升序列长度+1后以i为结尾的上升序列个数
                res = (res + num[i])%mod;
            }
        }
        while(len--)printf(" 0");
        printf("\n");
    }
}

猜你喜欢

转载自blog.csdn.net/qq_25576697/article/details/81487641