1. 程式人生 > >Gym101889J. Jumping frog(合數分解+環形dp預處理)

Gym101889J. Jumping frog(合數分解+環形dp預處理)

比賽連結:傳送門

題目大意:

  一隻青蛙在長度為N的字串上跳躍,“R”可以跳上去,“P”不可以跳上去。

  字串是環形的,N-1和0相連。

  青蛙的跳躍距離K的取值範圍是[1, N-1],選定K之後不可改變。

  要求青蛙最後能跳回起點(起點可以是0-N-1的任意一個位置),問K的取值有多少種選擇。

  3≤N≤105

 

思路:

  考慮到如果gcd(N, K) = g,則從起點開始跳的話,所有經過的點都是g的倍數,而且每個g的倍數都會經過。

  所以只要考慮從任意一個點i開始,步長為g地跳,能不遇見"P"而跳到N+i的位置的話,那麼這個K可以選。

  直接模擬的話就O(N2

logN)了,考慮優化。

  因為對於確定的g,對應的有很多個K,而這些K的選與不選是確定的,所以考慮列舉g(其實就是N的約數),對每個g預處理出它是否能滿足題意地完成條件。

  沒記錯的話約數的數量應該是logN級別的,所以環形dp預處理的複雜度為O(NlogN)。

  然後列舉一遍K,更新答案就可以了。

  複雜度O(NlogN + NlogN)。

 

程式碼:

#include <bits/stdc++.h>

using namespace std;
const int MAX_N = 1e5 + 5;

int N;
char S[MAX_N];

vector 
<int> factor; void getFactors(int N) { factor.clear(); for (int i = 1; i <= N/i; i++) { if (N%i == 0) { factor.push_back(i); if (i*i != N) factor.push_back(N/i); } } sort(factor.begin(), factor.end()); } bool f[MAX_N << 1
][50]; bool can_jump[MAX_N]; void dp() { int cnt = factor.size(); memset(f, false, sizeof f); for (int i = 1; i <= 2*N; i++) { for (int j = 0; j < cnt; j++) { int tmp = factor[j]; if (S[(i-1)%N] == 'P') f[i][j] = false; else if (S[(i-1)%N] == 'R') { if (i-tmp <= 0) f[i][j] = true; else f[i][j] = f[i-tmp][j]; } } } memset(can_jump, false, sizeof can_jump); for (int i = N+1; i <= 2*N; i++) { for (int j = 0; j < cnt; j++) { int tmp = factor[j]; if (f[i][j]) can_jump[tmp] = true; } } } inline int gcd(int a, int b) { return a%b ? gcd(b, a%b) : b; } int main() { scanf("%s", S); N = strlen(S); getFactors(N); dp(); int ans = 0; for (int k = 1; k <= N-1; k++) { int g = gcd(k, N); if (can_jump[g]) ans++; } cout << ans << endl; return 0; }
View Code