1. 程式人生 > 實用技巧 >南陽ccpc C題 The Battle of Chibi(樹狀陣列優化+dp)

南陽ccpc C題 The Battle of Chibi(樹狀陣列優化+dp)

題意:

給你一個長度為n的陣列,你需要從中找一個長度為m的嚴格上升子序列

問你最多能找到多少個

題解:

我們先對原序列從小到大排序,排序之後的序列就是一個上升序列

這裡如果兩個數相等的話,那麼因為題目要我們求嚴格上升子序列,所以我們讓這個數在陣列中原來位置靠後的排序之後讓它靠前(靠前也就是下標小)

我們dp方程:dp[i][j]表示截至到第i(這個i是按照沒排序之前的下標)個元素,上升子序列長度為j的子序列能找到dp[i][j]個

dp轉移方程:dp[i][j]=dp[1--i-1][j-1]

dp[1--i-1][j-1]就表示dp[1][j-1]+dp[2][j-1]+...+dp[i-1][j-1]

可以說就是求字首和,這裡用的是樹狀陣列維護的

比如原序列為:3 11 5 2 6

排序後序列為:2 3 5 6 11

按照排序後這個順序進行dp,當dp到11的時候,是dp[2][j]的值改變,序列中3,5,6的dp[i][j]中的i大於2,所以不會多求或者少求

而對於dp到6這個數,因為3,5,2這三個數在原序列中的位置就比它靠前,所以輪到求dp[5][j]的時候,2,3,5的dp值都已經求出來了

那麼這個時候求出來的字首和dp[1--4][j]就是正確的

這個自己可以模擬看下

程式碼:

#include<iostream>
#include<algorithm>
#include
<cstdio> #include<queue> #include<map> #include<vector> #include<cstring> using namespace std; const int mod=1e9+7; const int maxn=1e3+5; #define mem(a) memset(a,0,sizeof(a)) //求sum(dp[1-x][j]) int n,m,dp[maxn][maxn]; struct shudui { int id,val; }que[maxn]; bool cmp(shudui x,shudui y) {
if(x.val!=y.val) return x.val<y.val; return x.id>y.id; //如果兩個val相等,因為題目要求嚴格遞增,所以這樣排序就可以滿足題意 } int lowbit(int x) { return x&(-x); } void update(int x,int y,int val) //更新包含dp[x][y]的 { //字尾陣列項 while(x<=n) { dp[x][y]=(dp[x][y]+val)%mod; x+=lowbit(x); } } int get_sum(int x,int y) { int sum=0; while(x>0) { sum=(sum+dp[x][y])%mod; x-=lowbit(x); } return sum; } int main() { int t,p=0; scanf("%d",&t); while(t--) { mem(dp); scanf("%d%d",&n,&m); for(int i=1;i<=n;++i) { scanf("%d",&que[i].val); que[i].id=i; } sort(que+1,que+1+n,cmp); for(int i=1;i<=n;++i) { for(int j=1;j<=m;++j) { if(j==1) update(que[i].id,j,1); else //因為我們按照val排過序了,所以我們可以加上字首和就行 { int sum=get_sum(que[i].id-1,j-1); update(que[i].id,j,sum); } } } printf("Case #%d: %d\n",++p,get_sum(n,m)); } return 0; }