Codeforces 1371E Asterism

题目大意

(n) 个敌人,编号为 (1sim n),第 (i) 个敌人有 (a_i) 个糖果。Yuzu在最开始时有 (x) 个糖果。当Yuzu拥有的糖果数大于等于她此时面对的敌人的糖果数时,它可以击败这个敌人,并取得1个糖果,否则她将被敌人击败,并且什么也得不到。Yuzu希望对所有的敌人都取得胜利,请帮她重新安排 (n) 个敌人的出现顺序,即 (1sim n) 的一个合法的排列 (P)。让我们定义 (f(x)) 等于初始时Yuzu有 (x) 个糖果时这样的排列 (P) 的数量。给出 (n,p),其中 (p) 是质数,并且 (pleq n)。 我们称 (x) 是好的,当且仅当 (f(x)) 不能被 (p) 整除。找到所有好的 (x)

此题分为 Easy 和 Hard 两个版本。
Easy 版本 (2leq pleq nleq 2000,1leq a_ileq 2000)
Hard 版本 (2leq pleq nleq 10^5,1leq a_ileq 10^9)

题解

首先我们考虑 (x) 的范围。
因为要面对每个敌人都取得胜利,那么由于每胜利一次就获得一个糖果,则面对最后一个敌人时Yuzu所具有的糖果数是 (x+n-1)。那么显然,一定有 (x+n-1geq max{a_i})。当 (max{a_i}leq x),则说明 (x) 一开始就比所有的 (a_i) 大,随便怎么排都是合法的,排列数量是 (n!),但是 (pleq n),则 (p|n!),这样的 (x) 都是不好的。 综上,有(max{a_i}-n+1leq xleq max{a_i}),即可能是好的 (x) 最多只有 (n) 个。

然后我们考虑如何去计算 (f(x))
由于每击败一个敌人获得一个糖果,Yuzu面对第 (i) 个敌人时所具有的糖果数实际上是 (x+i-1),以 (i) 为横坐标,Yuzu的糖果数为纵坐标,这实际上是一条从 ((1,x))((n,x+n-1)) 的线段,是一个直角梯形的形状。要击败每一个敌人,即在排列 (P) 中,每个位置上的敌人的糖果数必须都在这条线段下方。那么贪心地想,我们首先考虑把糖果数最多的敌人放到这条线段下方,先把所有(a_i)从小到大排序,那么把糖果数最多的敌人放到这条线段下方的方案数是 (x+n-1-a_n+1=x+n-a_n),然后把糖果数次多的敌人放到这条线段的下方,方案数是(x+n-1-a_{n-1})。如此继续下去,当 (a_ileq x) 时,在剩下的位置随便怎么排都可以。然后根据乘法原理,即可得出答案。对于每一个 (x),我们可以很容易地在 (O(n)) 时间内求得 (f(x)),然后判断 (f(x)) 是否被 (p) 整除。由于 (x) 最多只有 (n) 个,这样做的时间复杂度是 (O(n^2)),可以通过 Easy 版本。

注意到按我们之前的从大到小的放置方法((a_i) 仍先从小到大排序),当 (a_ileq x) 时,在剩下的位置随便怎么排都可以,不妨设 (pos) 是使得 (a_{pos}leq x) 的最大的数,那么把 (a_1sim a_{pos}) 放到线段下方的方案数是 (pos!),因此当 (posgeq p) 时,一定有 (p|pos!),这种 (x) 一定是不好的。所以对于每一个 (x),我们可以先二分找到这样的 (pos),然后判断是否有 (posgeq p)。否则,注意到 (p) 是质数,由于(p mid pos!),若 (x) 是不好的,必有 (p) 整除把 (a_{pos+1}sim a_{n}) 放到线段下方的方案数。注意到方案数实际上是(x+n-a_n)(x+n-1-a_{n-1}),即 ((x+n)-(a_{n-i}+i)) 相乘的形式,我们只需判断是否存在一个 ((x+n)-(a_{n-i}+i)) ,它能被 (p) 整除即可。那么必然有 (x+nequiv a_{n-i}+i (mod p))。注意到对于特定的 (x)((x+n) mod p=r) 是常数,我们只需判断是否存在 ((a_{n-i}+i) mod p=r)。我们可以预处理出 ((a_{n-i}+i)mod p) ,令(Suf[m]) 表示使得模数为 ((a_{n-i}+i)mod p=m) 的最大的 (n-i),那么只要有 (Suf[r]> pos),就说明存在 (x+nequiv a_{n-i}+i (mod p)),即 (p|f(x))(x) 不是好的数。对于每一个 (x),这一判断可以 (O(1)) 完成,再加上之前二分的 (O(log n)),总的时间复杂度是 (O(nlog n)),可以通过 Hard 版本。

Code (Easy版本)

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
using namespace std;

#define RG register int
#define LL long long

template<typename elemType>
inline void Read(elemType &T){
    elemType X=0,w=0; char ch=0;
    while(!isdigit(ch)) {w|=ch=='-';ch=getchar();}
    while(isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
    T=(w?-X:X);
}

int n,p;
int Data[2005];
vector<int> Ans;

int Calc(int x){
    int Res=1LL,num=0;
    int L=x,R=x+n-1;
    for(RG i=n;i>=1;--i){
        if(Data[i]<=L){
            if(n-num<=0) Res=0;
            else Res=Res*(n-num)%p;
        }else{
            if(R-Data[i]+1-num<=0) Res=0;
            else Res=Res*(R-Data[i]+1-num)%p;
        }
        ++num;
    }
    return Res;
}

int main(){
    Read(n);Read(p);
    for(RG i=1;i<=n;++i)
        Read(Data[i]);
    sort(Data+1,Data+n+1);
    for(RG x=max(1,Data[n]-n+1);x<Data[n];++x){
        if(Calc(x)) Ans.push_back(x);
    }
    int len=Ans.size();
    printf("%d
",len);
    for(RG i=0;i<len;++i){
        printf("%d",Ans[i]);
        if(i<len-1) printf(" ");
    }
    printf("
");
    return 0;
}

Code (Hard 版本)

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
using namespace std;

#define RG register int
#define LL long long

template<typename elemType>
inline void Read(elemType &T){
    elemType X=0,w=0; char ch=0;
    while(!isdigit(ch)) {w|=ch=='-';ch=getchar();}
    while(isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
    T=(w?-X:X);
}

int n,p;
int Data[100005],DataB[100005],Suf[100005];
vector<int> Ans;

int Calc(int x){
    int Res=1LL,num=0;
    int L=x,R=x+n-1;
    int m=(R+1)%p;
    int pos=upper_bound(Data+1,Data+n+1,L)-Data-1;
    if(pos>=p) return 0;
    if(Suf[m]>pos) return 0;
    return 1;
}

int main(){
    Read(n);Read(p);
    for(RG i=1;i<=n;++i)
        Read(Data[i]);
    sort(Data+1,Data+n+1);
    for(RG i=n;i>=1;--i){
        DataB[i]=(Data[i]+n-i)%p;
        Suf[DataB[i]]=max(Suf[DataB[i]],i);
    }
    for(RG x=max(1,Data[n]-n+1);x<Data[n];++x){
        if(Calc(x)) Ans.push_back(x);
    }
    int len=Ans.size();
    printf("%d
",len);
    for(RG i=0;i<len;++i){
        printf("%d",Ans[i]);
        if(i<len-1) printf(" ");
    }
    printf("
");
    return 0;
}
原文地址:https://www.cnblogs.com/AEMShana/p/13223042.html