题意
给定一个数字串,每个位子都能向(i*i+1)%n的位子转移,输出在路径上、字典序最大的、长度为n的串($n \leq 150000$)。
分析
先考虑一个暴力的方法,考虑暴力每个x,然后O(n)判定形成的字符串字典序是否比当前的最优解要大,复杂度O(n²),显然大家都会做。
而本题中有个结论:没有必要每次O(n),只要前100个字符一样,那么后面的一定都一样!所以>500直接break,复杂度O(500n), 可以过!
理解:对于所有的下标k,k向(k*k+1)%n连一条有向边,最后可以得到若干棵基环树构成的森林,这个森林有三个性质:①基环树特别的多;②每棵基环树环特别的小;③叶子巨多;这样子的话,字符串一定会很快进入一个环,两个字符串前面相等后面就一定都相等了.
回头想一下,当时就应该“乱搞”,这种图肯定比较简单,因为出题人也没法特意搞极端情况,这是由数字的性质决定的。
#include<bits/stdc++.h> using namespace std; const int maxn = 150000 + 10; char s[maxn]; int n; int nxt[maxn]; int main() { int T, kase=0; scanf("%d", &T); while(T--) { scanf("%d", &n); scanf("%s", s); for(int i = 0;i < n;i++) nxt[i] = (1LL * i * i + 1) % n; char start = ‘0‘; for(int i = 0;i < n;i++) if(s[i] > start) start = s[i]; int res = 0; for(int i = 0;i < n;i++) { int cur = i, p = res; if(s[i] == start) { for(int j = 1;j < 100;j++) { if(s[cur] < s[p]) break; if(s[cur] > s[p]) {res = i; break;} cur = nxt[cur]; p = nxt[p]; } } } printf("Case #%d: ", ++kase); for(int i = 0;i < n;i++) { printf("%c", s[res]); res = nxt[res]; } printf("\n"); } return 0; }
参考链接:https://blog.csdn.net/Jaihk662/article/details/81987188?tdsourcetag=s_pctim_aiomsg
原文地址:https://www.cnblogs.com/lfri/p/11707209.html
时间: 2024-10-08 05:34:55