題目
給定一個n,p(2<=p<=n<=1e5),保證p是素數
以下一個長度爲n的數組a[],第i個數ai(1<=ai<=1e9),代表第i個人手中的糖果數
設Yuzu初始有x塊糖,以下Yuzu要參加一個比糖果多少的遊戲,
她可以預先指定這個比的順序,也就是1-n這n個人的排列,
如果Yuzu的糖大於等於對方的糖,Yuzu就贏了,
並且本輪中還會獲得一塊糖,然後再比下一輪
Yuzu想贏得所有的n輪遊戲,設能滿足其要求的n個人的排列數爲f(x)
若f(x)不是p的倍數,則稱x是"好"的,
求"好"的x的個數,並輸出所有的x
思路來源
1、2來自官方題解,3來自題解下方評論區及羣友
題解
1、2:
設mx=max{ai},也就是最多的糖果數
首先,注意到,(不要在意等號這些細節,可能會差1)
所需的糖數x不會少於mx-n,否則把mx放到最後一位也比不贏,
也就意味着所有的場次都比不贏, f(x)=0,%p==0
所需的糖數x不會多於mx,否則怎麼安排順序都會贏,f(x)=n!,%p==0
也就是,只需要在[mx-n,mx]這個長度爲n的區間檢查答案
記num[i]爲糖果數不超過i的人數,,
對應了i從0到n-1時,當前糖果數爲x+i時,每次num[x+i]-i個人可以挑戰
f(x)不爲p整除,令g(i)=num[x+i]-i,即任意g(i)都不爲p整除,則g(i)取值應均在[1,p-1],
考察g函數的性質,
若固定x,前一項隨着i的增加而遞增(非嚴格),後一項減1,所以g(i)要麼遞增,要麼最多減1,
若對於某一個x,畫出g的函數圖像,發現x遞增的時候,函數圖像會向上平移(非嚴格),
即在時的g(i),一定不超過時的g(i),
所以,答案應該是連續的一段,從某個值函數圖像均落在[1,p-1]起,到往上平移,挪出這個區間
題解1:
令x的左界lb=mx-n+1,如果對於當前i,num[lb+i]-i<1,說明應該增加下界,使g(i)更大,
不斷增lb,直至當前的i合法,再檢查下一個i
令x的右界ub=mx-n+n,如果對於當前i,num[lb+i]-i>=p,說明應該減小上界,使g(i)更小,
不斷減lb,直至當前的i合法,再檢查下一個i
題解2:
考慮,以x+i代換i,有
即給定x,對於所有的i,都不存在i,滿足
對於x=lb,其需要檢查[lb,lb+n-1]的i,
對於x=lb+1,其需要檢查[lb+1,lb+n]的i,
注意到只有左右界的單點修改,區間滾動
那麼,就預先把x=lb的對應的n個插入桶中,判斷x=lb是否合法,
再更改一下左右界,再檢查lb+1,依次類推,直至檢查完ub,
題解3:
先將a數組從小到大排序,則num[x+i]-i<p,即num[x+i]<p+i,排序後的數組中有num[a[p+i-1]]=p+i,
即恰滿足能戰勝p+i個的下界是x=a[p+i-1],說明num[x+i]<num[a[p+i-1]],有x+i<a[p+i-1],即x<a[p+1-i]-i
對於所有p+1-i合法的下標,檢查該不等式,取min即得上界ub
下界同理,由於num[x+i]-i>=1,有num[x+i]>=i+1=num[a[i]],即x+i>=a[i],x>=a[i]-i,
對於所有i合法的下標,檢查該不等式,取max即得下界lb
心得
一個題補了一天gg,發現性質很重要,然而着手怎麼做也很重要,
本題性質:num[x+i]-i>=1,num[x+i]-i<p,x只可能在[mx-n,mx]之間
代碼1
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10,M=2e5+10;
int n,p,a[N],num[M],mx,lb,ub;
int main(){
scanf("%d%d",&n,&p);
for(int i=0;i<n;++i){
scanf("%d",&a[i]);
mx=max(mx,a[i]);
}
//mx-n<=x<=mx 偏移量挪動到[0,n]
for(int i=0;i<n;++i){
num[max(0,a[i]-(mx-n))]++;
}
for(int i=1;i<M;++i){
num[i]+=num[i-1];
}
lb=1;
//num[n+n] 所以用二倍下標
for(int i=0;i<n;++i){
while(num[lb+i]-i<1){
lb++;
}
}
ub=n;
for(int i=0;i<n;++i){
while(ub>0 && num[ub+i]-i>=p){
ub--;
}
}
if(lb>ub)puts("0");
else{
printf("%d\n",ub-lb+1);
for(int i=lb;i<=ub;++i){
printf("%d ",i+(mx-n));
}
}
return 0;
}
代碼2
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10,M=2e5+10;
int n,p,a[N],num[M],mx;
int res[N],c,now[N];
int f(int x,int y){
return (x%y+y)%y;
}
int main(){
scanf("%d%d",&n,&p);
for(int i=0;i<n;++i){
scanf("%d",&a[i]);
mx=max(mx,a[i]);
}
//mx-n<=x<=mx 偏移量挪動到[0,n]
for(int i=0;i<n;++i){
num[max(0,a[i]-(mx-n))]++;
}
for(int i=1;i<M;++i){
num[i]+=num[i-1];
}
for(int i=0;i<n;++i){
now[f(i+(mx-n)-num[i],p)]++;
}
for(int i=0;i<=n;++i){
if(now[f(i+(mx-n),p)]==0){
res[++c]=i+(mx-n);
}
now[f(i+(mx-n)-num[i],p)]--;
now[f((n+i)+(mx-n)-num[i+n],p)]++;
}
printf("%d\n",c);
for(int i=1;i<=c;++i){
printf("%d ",res[i]);
}
return 0;
}
代碼3
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int n,p,a[N],lb,ub;
int main(){
scanf("%d%d",&n,&p);
for(int i=0;i<n;++i){
scanf("%d",&a[i]);
ub=max(ub,a[i]);
}
sort(a,a+n);
for(int i=0;i<n;++i){
lb=max(lb,a[i]-i);
}
for(int i=0;p+i-1<n;++i){
ub=min(ub,a[p+i-1]-i-1);
}
if(lb>ub)puts("0");
else{
printf("%d\n",ub-lb+1);
for(int i=lb;i<=ub;++i){
printf("%d ",i);
}
}
return 0;
}