Atcoder ARC 077 F
阿新 • • 發佈:2019-02-13
題意:
定義一個
f(s) 為將字串s
後面加上長度最小的串,使得新串仍時一個形如AA
的串。在操作無限次後求[L,R] 中各個字母出現的次數。保證原串形如AA
。
資料範圍:
2≤|S|≤2×105 1≤l≤r≤1018
演算法:
問AK爺怎麼做,被怒D
這不是傻逼題嗎。考慮串是怎麼轉移的:
- 設T為字串S的一個最小border(就是S的一個字首)
我也不知道這個是不是border- 那麼轉移就非常顯然了:
- SS -> STST -> STSSTS ……
- 由於是無限次操作,那麼只考慮原串的一半也是一樣的…
很顯然- S -> ST -> STS -> STSST……
- 觀察一下發現轉移的字串竟然時“斐波那契”字串
不知道有沒有這種字串,反正長得像就好了- 因為斐波那契的第80項左右好像已經大於
1018 所以暴力討論一下L,R
的情況就好了
題解寫成這樣估計又要被D
程式碼:
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 2e5 + 10;
int n, T, p[N];
char s[N];
long long L, R, num[N][30];
long long fib(long long len, int c) {
if (len <= n) return num[len][c];
if (len <= n * 2) return num[n][c] + num[len - n][c];
long long f1 = num[n][c], f2 = num[n][c] + num[T][c], l1 = n, l2 = n + T;
while (len > l1 + l2) {
long long t = f2;
f2 += f1, f1 = t;
t = l2, l2 += l1, l1 = t;
}
return f2 + fib(len - l2, c);
}
long long calc(long long len, int c) {
if (len <= n) return num[len][c];
if (n % T == 0) {
long long re = 1ll * (len - n) / T * num[T][c] + num[n][c];
len -= n, len %= T;
return re + num[len][c];
}
else return fib(len, c);
}
int main() {
scanf("%s%lld%lld", s + 1, &L, &R);
n = strlen(s + 1) / 2;
p[1] = 0;
for (int j = 0, i = 2; i <= n; i ++) {
for (; j && s[j + 1] != s[i];) j = p[j];
if (s[j + 1] == s[i]) j ++;
p[i] = j;
}
for (int i = 1; i <= n; i ++) {
for (int j = 0; j < 26; j ++) num[i][j] = num[i - 1][j];
num[i][s[i] - 'a'] ++;
}
T = n - p[n];
for (int i = 0; i < 26; i ++) printf("%lld%c", calc(R, i) - calc(L - 1, i), i == 25 ? 10 : 32);
return 0;
}