4361: isn
Time Limit: 10 Sec Memory Limit: 256 MBSubmit: 401 Solved: 199
[ Submit][ Status][ Discuss]
Description
给出一个长度为n的序列A(A1,A2...AN)。如果序列A不是非降的,你必须从中删去一个数,
这一操作,直到A非降为止。求有多少种不同的操作方案,答案模10^9+7。
Input
第一行一个整数n。
接下来一行n个整数,描述A。
Output
一行一个整数,描述答案。
Sample Input
4
1 7 5 3
1 7 5 3
Sample Output
18
HINT
1<=N<=2000
Source
思路:dp[i][j]表示长度为i,结尾为位置j的非降序列个数,sum[i]表示长度为i的非降序列个数,dp通过离散化+树状数组计算,不赘述。最后利用sum数组得出答案,对于剩余长度为i的贡献个数,为sum[i]*fac[n-i]减去非法的个数sum[i+1]*fac[n-i-1]*(i+1),因为非法的操作一定是从i+1转移到i的,对于每个i+1序列,恰好贡献了(i+1)*fac[n-i-1]次非法操作数,所以直接减掉它就是合法的个数。
# include <iostream>
# include <cstdio>
# include <algorithm>
using namespace std;
typedef long long LL;
const LL mod = 1e9+7;
const int maxn = 2e3+3;
LL dp[maxn][maxn], s[maxn][maxn], fac[maxn]={1LL}, sum[maxn];
int a[maxn], b[maxn];
void update(int len, int pos, LL num)
{
for(int i=pos; i<maxn; i+=i&-i)
{
s[len][i] += num;
if(s[len][i] >= mod) s[len][i] -= mod;
}
}
LL query(int len, int pos)
{
LL res = 0;
for(int i=pos; i>0; i-=i&-i)
{
res += s[len][i];
if(res >= mod) res -= mod;
}
return res;
}
int main()
{
int n, m;
scanf("%d",&n);
m = n;
for(int i=1; i<=n; ++i) fac[i] = fac[i-1]*i%mod;
for(int i=1; i<=n; ++i)
{
scanf("%d",&a[i]);
b[i]= a[i];
}
sort(b+1, b+1+n);
m = unique(b+1, b+1+n)-b-1;
for(int i=1; i<=n; ++i)
a[i] = lower_bound(b+1,b+1+m,a[i])-b;
for(int i=1; i<=n; ++i)
dp[1][i] = 1LL;
for(int i=2; i<=n; ++i)
{
update(i-1, a[i-1], dp[i-1][i-1]);
for(int j=i; j<=n; ++j)
{
dp[i][j] += query(i-1, a[j]);
if(dp[i][j] >= mod) dp[i][j] -= mod;
update(i-1, a[j], dp[i-1][j]);
}
}
for(int i=1; i<=n; ++i)
for(int j=i; j<=n; ++j)
{
sum[i] += dp[i][j];
if(sum[i] >= mod) sum[i] -= mod;
}
LL ans = 0;
for(int i=1; i<=n; ++i)
{
LL tmp = sum[i]*fac[n-i]%mod - sum[i+1]*fac[n-i-1]%mod*(i+1)%mod;
tmp = (tmp + mod)%mod;
ans = (ans + tmp)%mod;
}
printf("%lld\n",ans);
return 0;
}
本文介绍了一道算法题目,要求计算将一个序列变为非降序列的不同操作方案数量,并提供了详细的解题思路与代码实现。
570

被折叠的 条评论
为什么被折叠?



