2012-10-20 NOIP模擬賽“高級打字機”O(n)離線算法

題目:codevs3333 高級打字機

【題目描述】
早苗入手了最新的高級打字機。最新款自然有着與以往不同的功能,那就是它具備撤銷功能,厲害吧。請爲這種高級打字機設計一個程序,支持如下3種操作:
1.T x:在文章末尾打下一個小寫字母x。(type操作)
2.U x:撤銷最後的x次修改操作。(Undo操作)(注意Query操作並不算修改操作)
3.Q x:詢問當前文章中第x個字母並輸出。(Query操作)
文章一開始可以視爲空串。
【輸入格式】
第1行:一個整數n,表示操作數量。
以下n行,每行一個命令。保證輸入的命令合法。
【輸出格式】
每行輸出一個字母,表示Query操作的答案。
【樣例輸入】
7
T a
T b
T c
Q 2
U 2
T c
Q 2
【樣例輸出】
b
c
【數據範圍】
對於40%的數據 n<=200;
對於100%的數據 n<=100000;保證Undo操作不會撤銷Undo操作。
<高級挑戰>
對於200%的數據 n<=100000;Undo操作可以撤銷Undo操作。
<IOI挑戰>
必須使用在線算法完成該題。

分析:

這道題很厲害,我還是第一次遇到有200%的數據的題,見識了,但是更讓我覺得厲害的是它的若干解法。
對於100%的數據模擬,以及<IOI挑戰>的若干在線算法(有什麼主席樹啦,trie樹+倍增啦,rope啦)網上的dalao們講得很不錯,這裏我只是理解一下針對高級挑戰O(n)的離線操作。真實原因是隻寫得來這個。。。
首先引用題解上的幾句話:(題解

版本:接受第1--i個修改操作(包含Type和Undo)後的狀態稱爲版本i。版本0爲初始狀態。

版本鏈:一般的數據結構題目只有各種修改命令(比如本題的Type操作),那麼所有版本就會呈鏈狀排列。

這種情況下只需要設計一個合理的數據結構依次執行操作即可。


版本樹:Undo x撤銷最近的x次修改操作,實際上就是當前版本還原爲x次操作前的版本,換句話說,版本i = 版本i-x-1。


讀入所有操作並建樹,對這顆版本樹按歐拉序求出所有版本。上圖中就是按0->1->4...4->1->0->2->3->2->0的順序遍歷,同樣使用棧就能計算出所有的版本,然後在對應的版本上解決詢問即可。
那麼有了題解後,我們對着題解分析:
首先,第i次操作(查詢不算操作)後得到版本i。若第i次操作爲Type,我們從版本i-1向版本i建一條邊,並將操作i添加的字符壓入棧中(回溯時退棧),表示通過這次Type我們能從版本i-1到版本i,並且當前版本爲棧中字符串。
若第i次操作爲Undo,那我們的版本i = 版本i-x-1,那麼我們便從版本i-x-1連一條邊想版本i,表示版本i其實是可以從版本i-x-1得到的。
對於每一操作後若有查詢,便在該操作上記錄一下這個查詢以及這查詢在所有查詢中是第幾次。
對於樣例我們可以建圖:




(圖醜,莫笑)
所以沿着圖dfs一次就可以了,由於會遍歷到x條邊(就是x個操作),而x是與n差不多的,所以複雜度爲O(n)

代碼:

#include<cstdio>
#include<vector>
using namespace std;
const int maxn=100010;
int n,q,x,p,act;
int h[maxn],nxt[maxn],to[maxn],ans[maxn];
char ch,a[maxn],st[maxn];
vector <int> ques[maxn];
void build(int u,int v) {to[++p]=v;nxt[p]=h[u];h[u]=p;}
void dfs(int pos,int top)
{
    for(int i=h[pos];i;i=nxt[i]) dfs(to[i],top);//undo
    if(a[pos]) st[top]=a[pos],dfs(pos+1,top+1);//type
    for(int i=0;i<ques[pos].size();i++){//query
        int v=ques[pos][i];
        ans[v]=st[ans[v]];
    }
} 
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%s",&ch);
        switch (ch){
            case 'T':scanf("%s",&a[act++]);break;
            case 'U':{
            	scanf("%d",&x);act++;
    			build(act-x-1,act);
            	break;
            }
            case 'Q':{
            	scanf("%d",&x);
    			ques[act].push_back(++q);
    			ans[q]=x;
            	break;
            }
        }
    }
    dfs(0,1);
    for(int i=1;i<=q;i++) printf("%c\n",ans[i]);
    return 0;
}


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