• 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;
    }
    
  • 相关阅读:
    从Mono 4.0观C# 6.0部分新特性
    (译文)Python中的staticmethod与classmethod
    ubuntu中mysql中文乱码及用python3.x调用
    tornado学习 TCPClient 实现聊天功能
    tornado学习 TCPServer 实现聊天功能
    java基础知识3
    java基础知识5
    JSTL常用标签6
    Java基础知识2
    java基础知识4
  • 原文地址:https://www.cnblogs.com/AEMShana/p/13223042.html
Copyright © 2020-2023  润新知