打工

来源:互联网 发布:maxdos 网络启动版 编辑:程序博客网 时间:2024/04/28 07:27

这里写图片描述
Input
第一行,一个整数N表示参赛人数。
第二行,N个整数,表示询问的分队方式的序列。
Output
一行,一个整数表示这种方式会在第几天被采用。答案对1,000,007取模。
Sample Input
3
1 2 2
Sample Output
4
Data Constraint
对于100%的数据,N ≤ 10000 , 数据保证询问的数列是一个有效的序列。

Hint
比赛各天的分队情况如下:
第一天:1,1,1
第二天:1,1,2
第三天:1,2,1
第四天:1,2,2
第五天:1,2,3

The Solution

首先注意到一个简单的性质,
一个数 x 要出现在序列里,必须要
在它前面的位置出现 x-1。

可以注意到,一个序列中如果要出现 k 这个数,那么在他之前的
位置 1~k-1 必定都出现过,这样构造出的序列才是合法的。

举个例子 1,2,2,2….3….1,2,1,2….3

然后我们可以设f[i][j][0..1]表示前i个数,最大的数是j,
0表示 < j这个数,1表示 = 这个数。

普通转移很显然,f[i]只与f[i-1],f[i]只与f[i+1]有关,所以我们可以用滚动数组。

从后往前,考虑每一个位置,与原序列完全符合,或字典序比原序列小
那么方案数显然是
f[i+1][max(x,j)][z]=((ll)f[i+1][max(x,j)][z]+f[i][j][l])
z表示滚动0..1

计算方案数的式子长度为i,
写出来类似1jj.....1(j+1)(j+1)....1(j+2)(j+2)

这条式子很明显

因为你放一个数设为k,那么前面就有1~k的那么多个位置,再放入小于k的数的方案数显然为上述式子。

考虑 f[i]如何由 f[i-1]推来。当前这一位的后一位放 k 那么当前这
位就有 k 种放法;后一位放 k+1,就只有一种放法。

可以枚举最大值k

时间复杂度:O(N^2)

CODE

#include <cstdio>#include <iostream>#include <algorithm>#include <cstring>#define fo(i,a,b) for (int i=a;i<=b;i++)#define N 10005#define mo 1000007using namespace std;int n,a[N];int f[2][N][2],ans=0;// qian i ge shu zui da de shu shi j//0 < 1 =typedef long long ll;int main(){    freopen("data.in","r",stdin);    scanf("%d",&n);    fo(i,1,n) scanf("%d",&a[i]);    f[0][1][1] = 1;    int p=0,q=1;//o u    int mx = 1;    fo(i,1,n)    {        q = 1 - p;        mx = max(mx,a[i]);        fo(j,1,i)        {            f[q][j][1] = 0;            int tmp = max(0,j-a[i]+1) , t = min(j,a[i]-1);            f[q][j][0] =(f[p][j][0] * (ll)tmp + (f[p][j][0]+f[p][j][1]) * (ll)t + f[p][j-1][0]) % mo;            if (j < a[i]) f[q][j][0] += f[p][j-1][1];        }           f[q][mx][1] = 1;        p = q;    }    fo(i,1,n) ans = ((ll)ans + f[p][i][1] + f[p][i][0])%mo;    printf("%d\n",ans);    return 0;}
2 0
原创粉丝点击