F - Best Representation
给小写字母字符串S
问S能最少被切割成多少个
(?),使得每个子串都不能表示成循环节(ababa好,ababab坏)
有多少个方案 能 达到最小的切割数 mod 1e9+7
|S| 5e5
2s
256mb
我的思路
如果整个字符串是好的,那么答案为1,1
否则 整个字符串 如果是坏的,那么它可以表示成 字符串t的重复拼接
那么 最小循环节 长度如果 > 1,那么删去最后一个 是否是一个方案呢??
1 2 3
| [.....][.....][.....][.....][.....][.....] [...][...][...][...][...][...][...][...] x
|
注意到这个两个数差为1,所以gcd(len)=1
而它们相互间 多个不同偏移量的对应相等,将会像gcd过程一样对位相等
所以不可能
所以 最小分割一定是2
所以问题变成了,整个字符串有多少个切割让 left right 都是好的
同样的, 只要不是最小切割的倍数即可
所以感觉就过了?
还是有一点点问题
1 2 3
| 111222111222 xx yyyyyyyyyy
|
对于S的最小循环节 内还是有可能有更小的 循环的存在的
判断也好判断,如果要长度len的 循环节长度是cycle,那么len-cycle的循环节的长度是cycle的因数,注意前缀后缀都要算
然而 https://atcoder.jp/contests/arc060/submissions/50471607
ACx63,WAx2
subtask1_03.txt 和 subtask2_01.txt 炸了
1 2 3 4 5 6 7
| abaababaab len(s) = 10, cycle size = 5 brute force = 8 left cycle, right not cycle, [0,5][6,9] abaaba 2 9
|
这样的话虽然 baab是好的,但是左边是坏的且超过了单个循环节的长度
代码
https://atcoder.jp/contests/arc060/submissions/50473785
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85
| #include <bits/stdc++.h> using namespace std; typedef long long ll; #define rep(i,a,n) for (ll i=a;i<(ll)(n);i++) ll read(){ll r;scanf("%lld",&r);return r;} const int N=500000; char s[N+10]; vector<int> ch[N+10]; int subcycle(const string&t){ int sz=t.size(); for(auto i:ch[sz]) { assert(i < sz and sz%i == 0); bool cycle = true; rep(j,0,sz) if(t[j] != t[j%i]) {cycle = false;}; if(cycle) return i; } return t.size(); } template<class T> T reverse(const T &a){ int sz=size(a); T ret; rep(i,0,sz) ret.push_back(a[sz-1-i]); return ret; }
vector<bool> cut(const string&t){ int sz=t.size(); assert(sz>=2); vector<int> cyc(sz,0); iota(begin(cyc),end(cyc),1); rep(i,1,sz) { for(int l:ch[i+1]) { if(l == cyc[i-l]){ bool ok = true; rep(j,0,l) if(t[j] != t[i+1-l+j]) { ok = false; break; } if(ok) { cyc[i]=l; break; } } } } vector<bool> ret(sz,0); rep(i,0,sz) ret[i] = cyc[i]==i+1; return ret; }
int main(){ rep(i,1,N+1) rep(j,2,N+1) { if(i*j > N) break; ch[i*j].push_back(i); } scanf("%s",s); int sz = strlen(s); if(sz == 1) { printf("1\n1\n"); return 0; } string s0; rep(i,0,sz) s0.push_back(s[i]); int i = subcycle(s0); if(i == sz) { printf("1\n1\n"); }else if(i == 1) { printf("%d\n1\n",sz); }else{ if(sz/i > 2) { int ways = (sz/i-2)*(i-1); for(auto w:cut(s0.substr(0,i))) ways+=w; for(auto w:cut(reverse(s0.substr(sz-i,i)))) ways+=w; printf("2\n%d\n",ways-2); }else{ int ways = 0; auto w0 = cut(s0); auto w1 = reverse(cut(reverse(s0))); rep(i,0,sz-1) ways += w0[i] and w1[i+1]; printf("2\n%d\n",ways); } } return 0; }
|
总结
核心还是string 的自相似的一些性质
感觉 一定能切两段的证明,没有数学化掉,这个题解官方没有发布,感觉数学上 这题还不知道怎么搞
然后arc111+ 以后才有英文题解, 061~110
先跳过吧,没有反馈的训练还是不行,不知道洛谷有没有题解,之后再说