1. 程式人生 > >【單調佇列】【動態規劃】[CQBZOJ3059]Bead

【單調佇列】【動態規劃】[CQBZOJ3059]Bead

題目描述

Alex 喜歡玩網路遊戲,認為這是智力和體力的綜合鍛鍊。在一次遊戲活動中,
他意外獲得了一個傳說中威力極其強大的法寶:珠鏈。
珠鏈,顧名思義,就是由許多小珠子串起來的一條鏈。珠子有很多種顏色。
Alex 聽說過,只有將珠鏈打磨純淨,珠鏈才能發揮最大的威力。
純淨珠鏈是指這樣的珠鏈:它可以分成若干個長度相等的段,使任何兩段的
任何相同位置的珠子的顏色均不同,相同位置指珠子在段內的相對位置相同;而
且每段的長度以及劃分的段數也是有規範的,Alex 記得,每段包含的珠子數目
必須在L 到R 之間,而且劃分的段數不能少於S。
所謂打磨,就是從珠鏈的首和尾拿掉連續的若干個珠子。打磨後的純淨珠鏈
的威力等於它的每個珠子具有的魔力值之和。一個珠子的魔力值只與它在打磨前
的珠鏈中的位置有關。在查詢和分析了大量實驗資料以後,Alex 發現珠子的魔
力值等於珠子原來位置編號的約數個數!
興奮不已的Alex 想將珠鏈打磨成威力最大的純淨珠鏈。然而,馬上要參加
期末考試的Alex 來不及計算了,你能否幫助Alex 算出最大的威力值呢?

輸入
輸入檔名為bead.in。
第一行是四個整數N, L, R, S。
第二行是一個長度等於N 的字串,表示Alex 得到的珠鏈。字串的第i
個字元表示珠鏈的第i 個珠子的顏色。相同字母表示相同顏色。珠子的位置從1
編號到N。

輸出
輸出檔案為bead.out。
輸出一行,表示打磨後的純淨珠鏈的最大威力值。如果無法打磨成滿足要求
的純淨珠鏈,輸出-1.

樣例輸入

7 2 3 2
abcbcaa

樣例輸出

15

提示

能夠打磨出的合乎要求的純淨珠鏈有三種:bc/aa, abc/bca 和bcb/caa。其中威
力最大的是第三種,其威力值等於2+2+3+2+4+2 = 15。
如果給出的珠鏈是純淨珠鏈,那麼可以不打磨。純淨珠鏈必須能劃分成不少
於S 個等長的段且每段長度在L 到R 之間。
【資料規模和約定】
30% 的資料:1 ≤ N ≤ 100;
60% 的資料:1 ≤ N ≤ 100,000;
100% 的資料:1 ≤ N ≤ 500,000,1 ≤ L, R, S ≤ N, 0 ≤ R – L ≤ 10.
輸入的字串只包含大寫和小寫的英文字母。字母區分大小寫。

題目分析

首先我們列舉LLenR那麼我們可以發現我們有答案那麼我們可以預處理Lasti表示佇列中第i個元素的i相同的所有位置上的前一個相同的字母出現的位置那麼我們有答案就是max{sum[i]sum[i(iLastmaxi)+(iLastmaxi)%Len]}那麼我們證明一下這個答案的正確性,首先如果答案不是Lastmaxi那麼我們令當前區間的右節點的位置在L那麼我們首先可以發現對於[L,i]如果我們要成立那麼我們有必須要Last[p]<L那麼同理有Last[p]p>Last[p]那麼我們有如果LLastmaxi那麼得到必有p>L那麼我們必定有LL

ast[p]那麼我們發現不滿足條件了,那麼必定有L>Last[p]因為L儘量小那麼我們L=Lastmaxi+1那麼同理可證明L只能等於Lastmaxi+1時最優。
注:Lastmaxi表示[1,i]中最大的Last上文中p在後面表示的是Last[p]=Lastmaxi
那麼總的演算法時間複雜度就是O((RL)N)可以勝任本題目

程式碼

#include <cstdio> 
#include <cstring> 
#include <algorithm> 
using namespace std; 
const int MAXN = 500000; 
int vis[60][500010], ans=-1, s[MAXN+10], sum[MAXN+10], n, L, R, S; 
void solve(int l){ 
    int Max = 0; 
    for(int i=1;i<=n;i++){ 
        Max = max(Max, vis[s[i]][i%l]); 
        int Lent = i-Max; 
        if(Lent/l >= S){ 
            Lent -= Lent % l; 
            ans = max(ans, sum[i]-sum[i-Lent]); 
        } 
        vis[s[i]][i%l] = i; 
    } 
    memset(vis, 0, sizeof vis); 
} 
char str[MAXN+10]; 
int main(){ 
    scanf("%d%d%d%d%s", &n, &L, &R, &S, str+1); 
    for(int i=1;i<=n;i++){ 
        for(int j=i;j<=n;j+=i) sum[j] ++; 
        sum[i] += sum[i-1]; 
    } 
    for(int i=1;i<=n;i++){ 
        if(str[i] >= 'a' && str[i] <= 'z') s[i] = str[i] - 'a'; 
        else s[i] = str[i] - 'A' + 26; 
    } 
    for(int i=L; i<=R; i++) solve(i); 
    printf("%d\n", ans); 

    return 0; 
}