【CF-gym101889:J】Jumping frog(圆上跳----思维)

题目地址:https://codeforces.com/gym/101889

题目:


圆上有N个点,编号从0开始,R表示石头,P表示水池(不可跳到水池)。

问有多少个k,1≤k≤N-1,使得青蛙能某个点起跳并回到这个点,且途中不经过水池,若当前编号为i,下一跳编号为(i+k)%N

解题思路:


又回到起点,最少跳了一圈,最多跳了N圈,令途中经过的点的数目为mk,m是跳的次数,则有mk=tN,mk必须是N的倍数。

因为只需要回到起点一次,所以目的是要找一个最小的m使得mk是N的倍数。

先不考虑有水池的情况。

(1)当k是N的因子时,m=N/k,途经了圆上的部分点,且沿着圆跳完一圈之后必定会到起点,起点可以只从0~k-1枚举,因为从≥k之后枚举的话在圆上跳的时候还会经过k之前的点,是等价的。(不过剪枝之后这个对时间复杂度并无影响)

(2)当k和N互质时,m=N,此时必定途经了圆上的所有点

(3)当k不和N互质且不是N的因子时, 即gcd(N,k)!=1时 ,m=N*k/gcd(N,k),途经了圆上的部分点,此时的是(1)中k的整数倍(>1的整数倍)

还可以发现一个重要的性质,若k满足题目的条件,那么2k,3k...必定也满足条件,可以直接标记。

故只需要考虑(1)(2),而1和N互质,其他和N互质的数也是1的倍数,最终只需要考虑(1),1也是N的因子。

最终思路:

1~N-1得到N的因子们,判断每个因子是否满足条件,若满足条件,它的倍数必定也满足条件,加上标记和剪枝。其实1~N-1最终每个数都考虑到了,但并非单纯的暴力。

训练的时候想到了红字的性质,但是没想到因子,其实是没有完全利用到这个条件。

ac代码:


#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5+10;
char s[maxn];
bool vis[maxn] = {false};
vector<int> fac;
int n;
bool judge(int i, int k)
{
    int pos = i;
    while(1)
    {
        pos = (pos+k) % n;
        if(s[pos] == 'P') return false;
        if(pos == i) return true;
    }
}
int main()
{
    scanf("%s", s);
    n = strlen(s);
    for(int i = 1; i < n; i++)
        if(n % i == 0) fac.push_back(i);
    int ans = 0;
    for(int i = 0; i < fac.size(); i++)
    {
        //即使fac[i]被标记了,但是它的其他倍数可能未被标记
        for(int j = 0; j < fac[i]; j++)//只跳了一圈
        {
            if(s[j]=='R' && judge(j, fac[i]))
            {
                for(int p = 1; p*fac[i] < n; p++)
                {
                    if(!vis[p*fac[i]])
                    {
                        vis[p*fac[i]] = true;
                        ans++;
                    }
                }
                break;
            }
        }
    }
    printf("%d\n", ans);
    return 0;
}
 
发布了299 篇原创文章 · 获赞 81 · 访问量 10万+

猜你喜欢

转载自blog.csdn.net/Cassie_zkq/article/details/102690762