The Battle of ChibiTime Limit: 6000/4000 MS (Java/Others) Memory Limit: 65535/65535 K (Java/Others)Total Submission(s): 3091 Accepted Submission(s): 1117 Problem Description Cao Cao made up a big army and was going to invade the whole South China. Yu Zhou was worried about it. He thought the only way to beat Cao Cao is to have a spy in Cao Cao's army. But all generals and soldiers of Cao Cao were loyal, it's impossible to convince any of them to betray Cao Cao. Input The first line of the input gives the number of test cases, T(1≤100) . T test cases follow. Output For each test case, output one line containing Case #x: y, where x is the test case number (starting from 1) and y is the ways Gai Huang can select the information. Sample Input
2 3 2 1 2 3 3 2 3 2 1 Sample Output
Case #1: 3 Case #2: 0 Hint In the first cases, Gai Huang need to leak 2 information out of 3. He could leak any 2 information as all the information value are in increasing order. In the second cases, Gai Huang has no choice as selecting any 2 information is not in increasing order.Source The 2015 China Collegiate Programming Contest Recommend wange2014 | We have carefully selected several similar problems for you: 6460 6459 6458 6457 6456 |
题意:给定一个长度为N的数列A,求A中有多少个长度为M的严格递增子序列。
分析:
设dp[i][j]以前j个数组成并以为a[j]为结尾的长度为i的严格递增子序列,
则状态转移方程:
i和j可以看出阶段,只会从小到大转移。k是DP的决策,两个限制条件:k < j 和a[k] < a[j] 。
可以先写出dp的朴素程序:
// 例题:The Battle of Chibi // 暴力枚举决策 const int mod = 1000000007; memset(f, 0 ,sizeof(f)); a[0] = -(1<<30); // -INF f[0][0] = 1; for (int i = 1; i <= m; i++) for (int j = 1; j <= n; j++) for (int k = 0; k < j; k++) if (a[k] < a[j]) f[i][j] = (f[i][j] + f[i-1][k]) % mod; int ans = 0; for (int i = 1; i <= n; i++) ans = (ans + f[m][i]) % mod;
我们发现j增加1时,k的取值范围0<=k<j 变为0<=k<j+1,也就是只多了k=j这个新决策,这样大大的浪费了时间,所以,我们要用到数据结构优化这个DP,那我们想想要优化什么?
我们发现我们可以维护一个支持如下操作的决策集合:1.插入一个新决策。具体来说,在j增加1前,把(a[j], dp[i-1][j])加入集合
2.给定一个a[j],查询满足a[k]<a[j]的对应dp[i-1][k]的和
以上我们就可以用树状数组来建立这一个集合了,不过a[i]的值可能很大,所以我们要对其离散化,是a[i]映射到【1,n+1】上来(其中初始化a[0]=-INF,使其离散化为1),我们用val(a[i])代表离散化后的值,建立树状数组
1.对于插入决策,就把val(a[k])的位置增加上dp[i-1][k]
2.对于查询操作,就是树状数组的【1,val(a[j])-1】的前缀和
这样的时间复杂度就变为(mnlogn)
注意:树状数组也应该mod
#include<stdio.h>
#include<string>
#include<string.h>
#include<cstdio>
#include<algorithm>
#include<iostream>
using namespace std;
typedef long long ll;
const int MAXN=1000+5;//最大元素个数
const int mod=1e9+7;
int m,n;//元素个数
ll c[MAXN];
int a[MAXN],b[MAXN],dp[MAXN][MAXN];//c[i]==A[i]+A[i-1]+...+A[i-lowbit(i)+1]
//返回i的二进制最右边1的值
int lowbit(int i)
{
return i&(-i);
}
//返回A[1]+...A[i]的和
ll sum(int x){
ll sum = 0;
while(x){
sum =(sum+c[x])%mod;
x -= lowbit(x);
}
return sum;
}
//令A[i] += val
void add(int x, ll val){
while(x <= n){
c[x] =(c[x]+val)%mod;
x += lowbit(x);
}
}
int main()
{
int t;
cin>>t;
int num=0;
while(t--)
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
b[i]=a[i];
}
sort(b+1,b+n+1);
int len=unique(b+1,b+n+1)-(b+1);
dp[0][0]=1;
for(int i=1;i<=m;i++)
{
memset(c,0,sizeof(c));
add(1,dp[i-1][0]); //val(a[o])=1
for(int j=1;j<=n;j++)
{
int pos=lower_bound(b+1,b+n+1,a[j])-b+1;
dp[i][j]=sum(pos-1);
add(pos,dp[i-1][j]);
}
}
int ans = 0;
for (int i = 1; i <= n; i++)
ans = (ans + dp[m][i]) % mod;
printf("Case #%d: %d\n", ++num, ans);
}
return 0;
}