1. 程式人生 > >Atcoder ARC 077 F

Atcoder ARC 077 F

題意:

定義一個f(s)為將字串s後面加上長度最小的串,使得新串仍時一個形如AA的串。在操作無限次後求[L,R]中各個字母出現的次數。保證原串形如AA

資料範圍:
  • 2|S|2×105
  • 1lr1018
演算法:

問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; }