bzoj 4104 解密運算 —— 思路
阿新 • • 發佈:2018-12-07
題目:https://www.lydsy.com/JudgeOnline/problem.php?id=4104
一開始發現了給出的順序是按這些末尾字元後面的字尾排序得到的;
然後發現可以一個一個把字串補全;
因為首先知道所有單個字元,排序後就是那些串的第一位,和末尾連起來,得到 n+1 個二元組;
然後把這 n+1 個二元組排序,再和末尾連起來,得到 n+1 個三元組;
以此類推,最後可以得到整個串,可惜這樣做是 n^2 的,空間也是;
考慮倍增?因為這樣得到的四元組的前兩個位置其實和當初得到的二元組一樣...
而且這個過程和字尾陣列的排序也很像啊!二元組是第二關鍵字,再加一個二元組是第一關鍵字...
然後想了一晚上也沒想明白怎麼倍增;
實際上,我又忘了字尾排序的另一種方法——(首字元相同的)去掉首字元,看後面字尾的排序;
這道題——給出的末尾字元是首字元的話,那麼它們已經按去掉首字元的字尾後排好序了;
所以把每行的字元以字元大小為第一關鍵字,行數為第二關鍵字排序,就知道在原串中這些首字元對應的字尾的排名了;
而這些行也是按字典序排序的;
所以知道了每一行末尾字元是哪一行的開頭;
於是從 “.” 開始,按排名對應回每一行,就倒著得到了原串!
在我原來的想法中,問題在於相同的字元作為開頭,無法知道誰對應哪個末尾;
而如果利用行的排名,相同字元之間的排名也明確了,就不需要每次再排序來確定對應的末尾字元;
字尾排序的那個方法(去掉首字元)...真常用啊...
程式碼如下:
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; int const xn=2e5+5; int n,m,rk[xn],s[xn],ans[xn]; struct N{int s,id;}p[xn]; bool cmp(N x,N y){return x.s<y.s||(x.s==y.s&&x.id<y.id);}bool cmp2(N x,N y){return x.id<y.id;} int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n+1;i++) scanf("%d",&s[i]),p[i].s=s[i],p[i].id=i; sort(p+1,p+n+2,cmp); for(int i=1;i<=n+1;i++)rk[p[i].id]=i; int top=0,nw=p[1].id; while(top<n+1)ans[++top]=s[nw],nw=rk[nw]; while(top>1)printf("%d ",ans[top--]); puts(""); return 0; }