[BZOJ4104][Thu Summer Camp 2015]解密运算
试题描述
对于一个长度为N的字符串,我们在字符串的末尾添加一个特殊的字符"."。之后将字符串视为一个环,从位置1,2,3,...,N+1为起点读出N+1个字符,就能得到N+1个字符串。
比如对于字符串“ABCAAA”,我们可以得到这N+1个串:
ABCAAA.
BCAAA.A
CAAA.AB
AAA.ABC
AA.ABCA
A.ABCAA
.ABCAAA
接着我们对得到的这N+1个串按字典序从小到大进行排序(注意特殊字符“.”的字典序小于任何其他的字符)结果如下:
.ABCAAA
A.ABCAA
AA.ABCA
AAA.ABC
ABCAAA.
BCAAA.A
CAAA.AB
最后,将排序好的N+1个串的最后一个字符取出,按照顺序排成一个新的字符串,也就是上面这个表的最后一列,就是加密后的密文“AAAC.AB”。
请通过加密后的密文求出加密前的字符串。
输入
第一行有两个整数N,M,分别表示加密前的字符串长度和字符集大小,其中字符用整数1,2,3,...,M编号,添加的特殊字符“."用0编号。
第二行为N+1个整数,表示加密后的字符串。
输出
输出仅一行,包含N个整数,用空格隔开,依次表示加密前字符串中每个字符的编号。
输入示例
6 3 1 1 1 3 0 1 2
输出示例
1 2 3 1 1 1
数据规模及约定
#i (i=1~4) N=5*(i+1) M<=3
#5~6 N,M<=50 字符串中字符互不相同
#7~8 N,M<=1000 字符串中字符互不相同
#9~12 N,M<=1000
#13~#20 N,M<=200000
题解
矩阵的最后一列排个序就一定是矩阵的第一列,然后对于矩阵的每一行进行一次轮换后再排一遍序仍然是原矩阵。所以对于第 i 列,它是从第 i-1 列通过一个固定的置换得到的,这个置换就是最后一列排序的置换,那么矩阵的第一行就可以从第一个位置逆着置换的边找一圈而得到了。
#include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <cctype> #include <algorithm> using namespace std; int read() { int x = 0, f = 1; char c = getchar(); while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); } while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); } return x * f; } #define maxn 200010 int n, m, A[maxn], Ws[maxn], nxt[maxn], ans[maxn]; int main() { n = read() + 1; m = read(); for(int i = 1; i <= n; i++) Ws[A[i] = read()]++; for(int i = 1; i <= m; i++) Ws[i] += Ws[i-1]; for(int i = n; i >= 1; i--) { int pos = Ws[A[i]]--; nxt[pos] = i; } sort(A + 1, A + n + 1); int cnt = 0; for(int i = 1, j = 1; j <= n; i = nxt[i], j++) if(A[i]) ans[++cnt] = A[i]; for(int i = 1; i < cnt; i++) printf("%d ", ans[i]); printf("%d", ans[cnt]); return 0; }