【CSGRound2】逐夢者的初心(洛谷11月月賽 II & CSG Round 2 T3)

題目描述#

給你一個長度爲\(n\)的字符串\(S\)html

\(m\)個操做,保證\(m≤n\)ios

你還有一個字符串\(T\),剛開始爲空。數組

共有兩種操做。測試

第一種操做:優化

在字符串\(T\)的末尾加上一個字符。spa

第二種操做:code

在字符串\(T\)的開頭加上一個字符。htm

每次操做完成後要求輸出有幾個\(l∈[1,T.size]\)知足如下條件:blog

對於\(∀i∈[1,l]\)\(T_{T.size−l+i}≠S_i\)隊列

\(Tip:\)字符串下標從\(1\)開始。\(T.size\)表示\(T\)的長度。

輸入格式#

第一行兩個正整數\(n,m\)

第二行\(n\)個正整數,用空格隔開,第\(i\)個整數表示\(S_i\)

接下來\(m\)行,每行兩個數字 \(opt,ch\)\(opt=0\)表示在\(T\)的末尾加一個字符\(ch\),\(opt=1\)表示在\(T\)的開頭加一個字符\(ch\)

輸出格式#

\(m\)行,每行一個非負整數表示第\(m\)操做後的輸出。

輸入輸出樣例#

輸入 #1

10 3
1 2 3 1 2 3 2 3 2 3
0 1
1 2
0 3

輸出 #1

0
1
1

說明/提示#

注意:本題採用捆綁測試,只有當你經過一個\(subtask\)的全部點後,你才能拿到這個\(subtask\)的分數

對於全部的數據 \(n≤10^6,m≤3.3333×10^4,|∑|≤10^3,S_i∈[1,|∑|]。(∑表示字符集)\)

\(subtask1(17\%):m≤333\)

\(subtask2(33\%):m≤3333\)

\(subtask3(20\%):|∑|≤2∣\)

\(subtask4(30\%):\)無特殊條件​

樣例解釋:#

第一次操做後,\(T=「1」\),

\(l=1\)\(T[1]=S[1]\),因此答案爲\(0\)

第二次操做後,\(T=「21」\),

\(l=1\)時,\(T[2]=S[1]\)

\(l=2\)時,\(T[1]!=S[1]\)\(T[2]!=S[2]\),因此答案爲\(1\)

第三次操做後,\(T=「213」\),

\(l=1\)時,\(T[3]!=S[1]\);

\(l=2\)時,\(T[2]=S[1]\);

\(l=3\)時,\(T[3]=S[3]\),因此答案爲\(1\)


\(PS:\) 如下是咱們機房的一個蒟蒻(文化課大佬%%%)lqx在博主的指導下撰寫的。

\(O(m^3)\)的作法很容易想,按照題意模擬便可。

預計得分\(17pts\)

對於\(O(m^2)\)的作法,由於這個題其實是查找\(S\)的前\(l\)個和\(T\)的後\(l\)個是否每一個都不相等,咱們考慮記錄\(dp[l]\)表示在上述意義下\(l\)是否合法(\(0\)表示都不相等,\(1\)表示至少有一個等)。容易知道,在\(T\)串最後插入一個字符時,由於\(S\)串始終不變,\(T\)串的最後\(l\)個字符從本來\(T\)串的後\(l\)個字符變成了本來\(T\)串的後\(l−1\)個字符加上新加入的字符,因此爲了比較新的\(T\)串後\(l\)個字符是否合法,咱們只須要比較新字符、本來\(T\)串的後\(l−1\)個字符是否有相等便可。即\(dp[i]=dp[i−1]|(ch==S[i])\)。這樣,對於每一個加入的字符,只須要用\(O(1)\)的複雜度檢查每一個枚舉到的\(l\)是否合法便可。

\(T\)串最前面插入一個字符時,由於本來的\(i\)個後綴依然沒有變化,只是增長了一個新的後綴\(i+1\),因此咱們只需暴力\(check\)新加入的後綴,對於每一位枚舉是否有相等的便可。

時間複雜度\(O(m^2)\),預計得分\(50pts\)

考慮優化\(O(m^2)\)的作法,咱們找到了狀壓神器——\(bitset\),它能夠將複雜度優化到原來 \(\dfrac{1}{w}\)(\(w\)爲計算機字長,通常爲\(32\)\(64\))。若是常數優秀一些這個方法能夠過。

考慮剛纔的方法算過了哪些不可能合法的狀態,咱們知道全部的字符其存在位置都是獨立的,因此咱們用\(|∑|\)\(bitset\)數組\(ch[x]\)記錄字符\(x\)\(S\)中的哪些位置上出現過。只要加入的新字符\(x\)對應的位置是\(ch[x]\)\(1\)的位置,則該狀態確定不合法。

因此這樣優化的關鍵在於同時算出了全部合法的狀態。因此咱們用\(dp\)的第\(i\)位的\(0/1\)表示後綴長度爲\(i\)時是否有相等的字符。

若是在\(T\)串尾部加入新的字符,則對於長度是\(i\)的狀況必定是由\(i−1\)的狀況和新加入位的狀況同時轉移來(見上述\(O(m^2)\)作法),而全部新加入的位對應與\(S\)串中哪些位相同已經存儲好,假設加入的字符是\(x\),則\(dp=(dp<<1)|ch[x]\)

若是在\(T\)串頭部加入新的字符,設原來\(T\)串有效的後綴長度有\(l\)位,則新的\(T\)串後\(l\)位是否合法狀態不變,因此新舊\(T\)串前面\(l\)位答案同樣;

\(T\)串頭部插入新字符時,咱們將問題拆成兩部分:

第一,咱們發如今頭部加入字符時,後面的全部字符都日後移了一位。

第二,咱們須要比較加入的新字符和第一個字符是否相同。

很明顯困難在於解決第一個問題。由於咱們若是要想比較移動以後的字符和\(S\)的關係,在不知道其它任何東西的狀況下,須要另用一個\(O(m)\)檢查。

解決這個問題的方法是一個很是重要的思想:費用提早。在每次加入一個字符時,咱們將這個字符所能貢獻的答案都記在\(dp\)中。方法很簡單,假設咱們每次加進的字符是\(x\),考慮這一位對應到\(S\)串的全部可能。若是\(x\)對應到的某一位上\(ch[x]\)在一樣的位上剛好是\(1\),說明加入字符使當前這個\(x\)剛好對應到剛纔的那一位上,則這樣的方案確定是不合法的。

考慮如何進行這樣的操做。假設\(x\)是在第\(i\)位加入隊列而且對應的\(ch\)值在第\(k\)位上是\(1\)\(dp[i-1+k]\)必定不合法(假設沒有從後插入的)。這時\(x\)距離隊尾的距離是\(i−1\),因此當\(x\)取到\(k\)時,隊尾應該到\(k+(i-1)\)位,可是注意在\(bitset\)裏是反着存的,因此:

\(dp=dp|(ch[x]<<i−1)\)
時間複雜度爲\(O(\dfrac{m^2}{w})\)

代碼實現:

#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<ctime>
#include<climits>
#include<algorithm>
#include<bitset>
using namespace std;
const int M=40000;
bitset<M> ch[1010],dp,limit;
int n,m;
int main()
{
    int i,t,x;
    scanf("%d%d",&n,&m);
    for(i=1;i<=m;i++) scanf("%d",&x),ch[x].set(i);
    for(;i<=n;i++) scanf("%d",&x);
    limit.set();
    for(i=1;i<=m;i++)
    {
        scanf("%d%d",&t,&x);
        limit.reset(i);
        if(t==0) dp=(dp<<1)|ch[x];
        else dp=dp|(ch[x]<<i-1);
        printf("%d\n",(~(dp|limit)).count());
    }
    return ~~(0-0);
}
相關文章
相關標籤/搜索