【CodeForces】582 C. Superior Periodic Subarrays

【题目】C. Superior Periodic Subarrays

【题意】给定循环节长度为n的无限循环数列,定义(l,s)表示起点为l的长度为s的子串,(l,s)合法要求将子串从该起点开始以s为循环节长度无限循环中每个数字都>=数列中对应位置的数字,求合法(l,s)的数量。0<=l<n,1<=s<=n,n<=2*10^5,1<=ai<=10^6。

【算法】数论,计数问题

【题解】先不考虑起点,对于选定的s,子串中的数字必须≥所有间隔为g=gcd(s,n)的数字(核心思想)

例如在1 2 3 4 5 6,g=2时,1 3 5为一组,2 4 6为一组,子串中如果包含某一组的一个,那个就必须≥整组的数字。

枚举g,对于数列中g个[数字间隔为g的序列],显然每个序列中只有最大的数字才有可能被选择为子串,那么处理后我们得到了01数列。

对于01数列,我们要用连续的1凑出长度s满足gcd(s,n)=g,先使用双指针统计出几段连续的1,对于每一段连续的1枚举长度贡献答案。

复杂度O(d(n)*n),其中d(n)为n的因子个数。

以上为CF官方题解,在处理01序列的时候可以采用一种较简单的写法:

枚举g,预处理c[x]表示长度i=1~x中满足g=gcd(i,n)的 i 的个数。

处理出01序列,然后将链复制一遍,倒着计算出b[i]表示从i出发最长的连续1个数。

最后枚举起点l,每个点贡献答案c[b[i]]。

#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=200010;
int n,a[maxn],b[maxn<<1],c[maxn],mx[maxn],gc[maxn];
int gcd(int a,int b){return !b?a:gcd(b,a%b);}
//learn from LIBERATOR
int main(){
    scanf("%d",&n);
    for(int i=0;i<n;i++)scanf("%d",&a[i]);
    for(int i=1;i<n;i++)gc[i]=gcd(i,n);
    long long ans=0;
    for(int g=1;g<n;g++)if(n%g==0){
        for(int i=1;i<=n;i++)c[i]=c[i-1]+(gc[i]==g);
        for(int i=0;i<g;i++)mx[i]=a[i];
        for(int i=g;i<n;i++)mx[i%g]=max(mx[i%g],a[i]);
        for(int i=0;i<n;i++)b[i]=b[i+n]=(mx[i%g]==a[i]);
        for(int i=n+n-2;i>=0;i--)if(b[i])b[i]+=b[i+1];
        for(int i=0;i<n;i++)ans+=c[min(n-1,b[i])];
    }
    printf("%lld",ans);
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/onioncyc/p/7944055.html