【洛谷5794】[THUSC2015] 解密运算(模拟)
大致题意: 对于一个字符串,我们在其末尾添加一个'.',将字符串视作一个环,则可以从\(n+1\)个位置断开得到\(n+1\)个新串。现将这\(n+1\)个新串按字典序排序('.'的字典序最小),按序给出排序后每个串末位字符,求原字符串。
前言
在一家咖啡店看到这题,在纸上乱涂乱画半天,最后口胡了出来。
不过当时做法比较复杂,后来吃饭时想了想给它整理了一下,最后写出来的做法就很简洁了。
手玩
首先我们来手玩一下样例,或许能从中发现什么。
样例给出的末位字符串是"AAAC.AB"。
通过这个末尾字符串,其实题目还间接给了我们一个信息,即这个字符串显然是由".AAAABC"这七个字符构成的。
而由于它是按字典序排列的,显然首位字符就应该按次是".AAAABC"。
把它写成一个方阵形式,就是:
由于字符串成环,也就是说,在上面的每一个字符串中,首位字符在原字符串里都跟在末位字符后面。
也就是说,'.'后面是'A','A'后面是'.'或'A'或'A'或'B','B'后面是'C','C'后面是'A'。
再次因为这些字符串是按字典序排列的,所以我们可以把每种字符后面能够跟着的那些字符排个序,然后再依次填入方阵中。
于是就得到了:
上面的做法似乎给了我们点启发,于是我们就可以套路地想到:
"A."后面跟着的是'A',"AA"后面跟着的是'.'或'A',"CA"后面跟着的是'A',".A"后面跟着的是'B',"AB"后面跟着的是'C',"BC"后面跟着的是'A'。
填入方格中,就得到:
按照这样的套路,我们就可以轻松把这个矩阵填完整了,剩下的请读者自己去尝试。
而如果把这个算法用程序实现,就是\(O(n^2)\)的复杂度,已经能够得到\(60\)分的好成绩了。
优化
接下来,我们该怎么优化呢?
如果你认认真真把样例给手玩完了,你或许可以发现一个规律:对于每一个串,它总是从同一个串转移过来的。
例如上面的第一个串,它的下一个字符总是从上面的第五个串得到的。
这让我们想到,如果能够直接求出每个串的字符从哪个串转移过来,这道题似乎就做完了。
而这其实是很好求的。
具体地,首先我们把题目中给出的末位字符排序,就得到首位字符串。
然后我们把每个字符串以末位字符为第一关键字、首位字符为第二关键字、编号为第三关键字再次排序,排序完后的第\(i\)个字符串的编号所对应的原先的字符串,就是向它转移的字符串。
如果把转移看作一条边,我们只要从第一个字符串出发,沿反向边遍历一遍,每次取原先字符串(即题目中按字典序排序的字符串)的首位字符(不难发现,原先字符串的首位字符是以首位字符为关键字排序后的结果),就是答案。
至于为什么这么做,有点难以描述,实在不清楚就自己去画画图吧,应该还是比较显然、好理解的。
代码
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 200000
using namespace std;
int n,m,a[N+5];struct data
{
int H,T,p;I data(CI x=0,CI y=0,CI t=0):H(x),T(y),p(t){}
I bool operator < (Con data& o) Con {return T^o.T?T<o.T:(H^o.H?H<o.H:p<o.p);}
}s[N+5];
class FastIO
{
private:
#define FS 100000
#define tc() (A==B&&(B=(A=FI)+fread(FI,1,FS,stdin),A==B)?EOF:*A++)
#define pc(c) (C==E&&(clear(),0),*C++=c)
#define tn (x<<3)+(x<<1)
#define D isdigit(c=tc())
int T;char c,*A,*B,*C,*E,FI[FS],FO[FS],S[FS];
public:
I FastIO() {A=B=FI,C=FO,E=FO+FS;}
Tp I void read(Ty& x) {x=0;W(!D);W(x=tn+(c&15),D);}
Tp I void write(Ty x) {W(S[++T]=x%10+48,x/=10);W(T) pc(S[T--]);}
Tp I void write(Con Ty& x,Con char& y) {write(x),pc(y);}
I void clear() {fwrite(FO,1,C-FO,stdout),C=FO;}
}F;
int main()
{
RI i;for(F.read(n),F.read(m),i=1;i<=n+1;++i) F.read(a[i]),s[i].T=a[i],s[i].p=i;//读入
for(sort(a+1,a+n+2),i=1;i<=n+1;++i) s[i].H=a[i];sort(s+1,s+n+2);//排序求出首位字符,然后再次排序
i=s[1].p;W(i^1) F.write(a[i],' '),i=s[i].p;return F.clear(),0;//遍历,输出首位字符,注意第一个'.'无需输出
}