题目
样例输入:
第一行,一个整数N表示参赛人数。
第二行,N个整数,表示询问的分队方式的序列。
3
1 2 2
样例输出:
一行,一个整数表示这种方式会在第几天被采用。答案对1,000,007取模。
数据范围:
对于100%的数据,N ≤ 10000 , 数据保证询问的数列是一个有效的序列。
详细情况见下表。
剖解题目
。。。。。
思路
这题好坑啊。。。。。
比赛时想了一会儿,看了看30分好拿。
发现可以dp,但看到n<=10000,就觉得n^2肯定过不了,想了半天又觉得我的dp似乎是n^3,就没有动手打。
然后交了个暴力水了30分。QwQ.
解法
30%:暴力!
其他部分分就不说了。
100%:有顺推与逆推。
我们发现第i位后的方案数至于第i位后的长度以及前i位的最大值有关。
逆推(粗略提一下,我没打):设f[i,j]表示前i位已经匹配完,且最大的ai是j的方案数,那么就有f[i,j]=f[i+1,j]*j+f[i+1,j+1];
初始化是f[n][i]=1;
顺推:差不多,f[i][j]=f[i-1][j]*j+f[i-1][j-1];然后由于每一位都有a[i]的限制,不需要全部处理。每次dp完i,就对f[i][max(1~i-1)]+=a[i]-1;就不需要初始化了。
注意:由于空间限制,一般是要滚动f的第一维,但我偷了点懒,将所有计算都转化为longlong的形态,就没有开滚动。
代码
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define down(i,a,b) for(int i=a;i>=b;i--)
#define ll long long
using namespace std;
const int maxn=10005,mo=1e6+7;
int f[maxn][maxn],n,a[maxn],c[maxn];
int main()
{
// freopen("T.in","r",stdin);
scanf("%d",&n);
fo(i,1,n) {
scanf("%d",&a[i]);
c[i]=max(a[i],c[i-1]);
//f[n][i]=1;
}
int ans=0;
fo(i,2,n){
fo(j,1,i) f[i][j]=((ll)f[i-1][j]*(ll)j+(ll)f[i-1][j-1])%mo;
f[i][c[i-1]]=((ll)f[i][c[i-1]]+(ll)a[i]-1)%mo;
}
fo(i,1,n) ans=((ll)ans+(ll)f[n][i])%mo;
printf("%d",ans+1);
}