BZOJ 1264 動態規劃 + 樹狀數組

   “很難想的一道題不過不難寫”—— Po


     這道題窩的思考過程十分坎坷。


    首先想着純DP,類似於NOIP Day2T2的方法搞。


    設f[i][j]爲強制將A[i]B串中第j個相同字符匹配的LCSs[i][j]爲考慮到A[i]B串中第j個相同字符匹配(且強制不允許選這個字符之後)的LCS


    從這個狀態設計就可以看得出來它既麻煩又sb

   

    先解釋一下爲什麼要強制不允許選該字符之後——否則會造成交叉:之前的超過了當前字符。

   

    這道題和NOIP2015 Day2T2有一個不同:前者是一個具有特殊性質的LCS問題,而後者需要有一個序列被完全匹配。

   

    所以本題用DP就會產生一系列的問題。比如上面所述的那個狀態就有一個很嚴重的問題:s[1][1]應該是什麼?如果B串中第一個A[i]同字符出現在較後面的位置,那麼s[1][1]甚至無法轉移——因爲它依賴於後面的狀態,比如A[2]B串中第一個同字符在A[1]之前,那麼就無法轉移了。

   

   但是由這個失敗的DP我們也能得到一些啓示:我們的DP順序應是按照B串中字符的順序。如此一來,我們的狀態也可以得到簡化:f[i]表示以i結尾的LCS長度。考慮LCS的基本特點:當且僅當a[i]=b[j]時,f[i]+1。那麼,我們就只需記錄A串中每個字符出現的5個位置,按照B串的順序掃描。對於B[j],我們找到A串中對應的5個位置,然後f[pos]=max{f[k]| k<pos}+1。注意要按k倒序搞,不然會造成重複加。如何取得最大值呢?線段樹用不着:對於A[1..m]的連續區間的最大值,直接用樹狀數組即可——考慮樹狀數組求和的原理:一層一層地往上爬,把沿途的長條的值累和。這裏,我們只要把每段“長條”維護的值改爲本區間內最大值即可。注意各種地方n*5別忘了。


// BZOJ 1264

#include <cstdio>
#include <cstring>
using namespace std;

 #define rep(i,a,b) for (int i=a; i<=b; i++)
 #define read(x) scanf("%d", &x)
 #define fill(a,x) memset(a, x, sizeof(a))
 #define dep(i,a,b) for (int i=a; i>=b; i--)

 const int N=20000+5, S=6;

 int a[N*5], b[N*5], n, C[N*5], cnt[N], pos[N][S], f[N*5], ans=0;

 int lowbit(int x) { return x&(-x); }

 int max(int a, int b) { return a<=b ? b : a; }

 int get_max(int x) {
 	int ret=0;
 	for( ; x; x-=x&-x) ret=max(ret, C[x]);
 	return ret;
 }

 void update(int x, int w) {
 	for( ; x<=n*5; x+=x&-x) C[x]=max(C[x], w);
 }

int main()
{
	read(n);
	rep(i,1,5*n) cnt[i]=C[i]=f[i]=0;
	rep(i,1,5*n) read(a[i]), pos[a[i]][++cnt[a[i]]]=i;
	rep(i,1,5*n) read(b[i]);
    rep(i,1,5*n) 
      dep(j,5,1) {
      	int p=pos[b[i]][j];
        f[p]=max(f[p], get_max(p-1)+1);
        update(p, f[p]);
        ans=max(ans, f[p]);
      }
    
    printf("%d\n", ans);
	
	return 0;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章