2019.11.11 洛谷月賽t3

題目背景

因爲Y校的老師很是毒瘤,要求\(zhouwc\)\(csp\)考前最後\(3\)天參加期中考,\(zhouwc\)很是生氣,決定消極考試,以塗完卡但全錯爲目標。如今\(retcarizy\)\(zhouwc\)太可憐了,想要幫\(zhouwc\)解決一個問題,但他本身又太忙了,咕咕咕,因而就把問題甩給了你。數組

題目描述

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

\(m\)個操做,保證\(m\leq n\)優化

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

共有兩種操做。code

第一種操做:隊列

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

第二種操做:字符串

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

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

對於\(\forall i \in [1,l]\)\(T_{T.size-l+i} \ne 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 \leq 10^6,m \leq 3.3333 \times 10^4,|\sum|\leq10^3,S_i \in [1,|\sum|]\)(\(\sum\)表示字符集)

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

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

\(subtask3(20\%):|\sum|\leq2∣\)

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

樣例解釋:

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

\(l=1\)\(T[1]=S[1]\),因此答案爲你不jjgvfj\(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\)

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

對於\(O(m^2)\)的作法,由於這個題其實是查找\(S\)的前\(l\)個和\(T\)的後\(l\)個是否嚴格不相等,咱們考慮記錄\(dp[l]\)表示在上述意義下\(l\)是否合法。容易知道,在\(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\)串最前面插入一個字符時,由於本來全部的合法的\(l\)依然沒有變化,只是增長了一個新的\(l\),因此咱們只需暴力\(check\)新加入的答案\(l\),對於每一位枚舉是否不一樣便可。

時間複雜度\(O(m^2)\),預計得分\(50pts\)。是我在考場上想出來的方法。

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

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

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

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

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

\(T\)串頭部插入新字符時,咱們發現遇到了一些新的問題:

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

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

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

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

考慮如何進行這樣的操做。假設\(dt\)是在第\(i\)位加入隊列,則\(dt\)\(T\)結尾的長度是\(i-1\)。注意這裏咱們只討論\(T\)序列結尾的費用提早,由於其它點狀況和結尾同樣。假設\(dt\)對應的\(id\)值在第\(k\)位上是\(1\),說明\(dt\)在取到第\(k\)位時總體必定不合法。這時\(dt\)距離隊尾的距離是\(l-1\),因此\(dt\)的位置由隊尾左移\(i-1\)位獲得,因此當\(dt\)取到\(k\)時,隊尾應該取到\(k+l-1\)位,可是注意是反着存的,因此:
\[ f=f|(id[dt]<<x-1) \]
上代碼:

#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<bitset>
using namespace std;
int n,m,opt,S[1000005],dt;
bitset<35005> f,id[1005],now;
int read(){
    int ans=0;
    char ch=getchar();
    while(ch<'0'||ch>'9')
        ch=getchar();
    while(ch<='9'&&ch>='0'){
        ans=ans*10+ch-'0';
        ch=getchar();
    }
    return ans;
}
int main(){
    n=read();
    m=read();
    for(int i=1;i<=n;i++)
        S[i]=read();
    for(int i=1;i<=m;i++)
        id[S[i]].set(i);
    now.set();
    for(int i=1;i<=m;i++){
        opt=read();
        dt=read();
        now.reset(i);
        if(opt==0)
            f=(f<<1)|id[dt];
        else
            f=f|(id[dt]<<(i-1));
        printf("%d\n",(~(f|now)).count());
    }
    return 0;
}
相關文章
相關標籤/搜索