HDOJ5542-The Battle of Chibi【詳細解釋樹狀陣列優化dp】
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;
}