Codeforces 827E Rusty String

一万年没有写过题解了...这题idea是不错

题意

给你一个只由V/K/?组成的字符串,问这个字符串的循环长度可以是多少,其中表示既可以是V也可以是K。将所有可能的循环长度都输出。

想法

如果没有?,那就是next数组判断。而之所以可以用next数组判断,是因为假设循环长度为\(d\),那么将字符串向右滑动\(d\)位之后重合的部分一定完全相同。基于这个理论,这道题也可以这么想。
假设循环长度为\(d\),将字符串向右滑动\(d\)位,重合的部分中,如果\(a_{d+i} == b_{i}(0 \leq i \leq n-1)\),则\(d\)可以被记录到答案中。
如果把b数组翻转一下是不是就很像卷积了?但是怎么做乘法呢?
我一开始想如果相等就为1,即两个1相乘,最后加起来如果是\(n-d\)就说明有效。但是这样VK就要分开看啊,没有办法进行一个统一的判断。
然后idea就来了...

for (int i = 0; i < n; i++) {
    if (s[i] == 'V') a[i] = 1;
    else if (s[i] == 'K') b[n - i] = 1;
}

这样的话...如果匹配就是0,如果加起来就是0那么可行...妙啊!
然后跑ntt/fft就好了。数据里面好像卡了一发长度为1的ntt?特判一下就好了。

Code

#include <bits/stdc++.h>
#define N (1 << 20)
#define ll long long
#define pb push_back
using namespace std;
const ll M = 998244353;
const ll g = 3; 
ll Pow(ll a, ll n) {
   ll ret = 1;
   while (n) {
      if (n & 1) ret = ret * a % M;
      n >>= 1;
      a = a * a % M;
   }
   return ret;
}
void rader(ll y[], int len) {
   for (int i = 1, j = len / 2; i < len - 1; i++) {
      if (i < j) swap(y[i], y[j]);
      int k = len / 2;
      while (j >= k) {
	     j -= k;
	     k /= 2;
      }
      if (j < k) j += k;
   }
}
void ntt(ll y[], int len, int on) {
   rader(y, len);
   for (int h = 2; h <= len; h <<= 1) {
      ll wn = Pow(g, (M - 1) / h);
      if (on == -1) wn = Pow(wn, M - 2);
      for (int j = 0; j < len; j += h) {
	     ll w = 1;
	     for (int k = j; k < j + h / 2; k++) {
	        ll u = y[k];
	        ll t = w * y[k + h / 2] % M;
	        y[k] = (u + t) % M;
	        y[k + h / 2] = (u - t + M) % M;
	        w = w * wn % M;
	     }
      }
   }
   if (on == -1) {
      ll t = Pow(len, M - 2);
      for (int i = 0; i < len; i++)
	 y[i] = y[i] * t % M;
   }
}
ll a[N], b[N];
int n, len;
char s[N];
int T;
bool ans[N]; 
vector <int> w; 
void init()
{
   w.clear();
   for (int i = 0; i <= len; i++) {
      a[i] = b[i] = ans[i] = 0;
   }
}
int main()
{
   cin >> T;
   while (T--) {
      scanf("%d%s", &n, s);
      if (n == 1) printf("1\n1");
      else {
	     len = 1; 
	     while (len <= n * 2) len <<= 1;
	     init(); 
	     for (int i = 0; i < n; i++) {
	        if (s[i] == 'V') a[i] = 1;
	        else if (s[i] == 'K') b[n - i] = 1;
	     } 
	     ntt(a, len, 1);
	     ntt(b, len, 1);
	     for (int i = 0; i <= len; i++)
	        a[i] = (a[i] * b[i]) % M;
	     ntt(a, len, -1);
	     for (int i = 1; i <= n; i++)
	        if (a[n - i] == 0 && a[n + i] == 0) ans[i] = true;
	     for (int i = 1; i <= n; i++) {
	        for (int j = i << 1; j <= n; j += i)
	           ans[i] &= ans[j];
	        if (ans[i]) w.pb(i);
	     }
	     cout << w.size() << endl;
	     for (int i = 0; i < (int)w.size(); i++)
	        printf("%d ", w[i]);
      }
      putchar('\n');
   }
   return 0; 
}
posted @ 2017-07-13 14:19  zkGaia  阅读(440)  评论(0编辑  收藏  举报