都这么会乱搞的吗/xia
随机生成若干 \(<30\) 的数直到它们当中和为 \(60\) 的子集个数 \(>k\) 为止。删去最后一个元素。然后考虑贪心确定 \(>30\) 的部分,具体方法是按照 \(dp_{60-x}\) 从大到小贪心选,如果剩余子集个数 \(\ge dp_{60-x}\) 就在序列中加入 \(x\)。如此随机化直到找到符合要求的序列为止。
证明的话不会,不过感性理解一下你随机出来的这个序列的 \(dp_{60-x}\) 应该近似于一个等比数列,有点类似于类比进制表示,所以你能随机出符合要求的序列的概率是很大的。
ll k;int ord[35],a[65];mt19937 rng(time(0));
int main(){
scanf("%lld",&k);
if(k<=60){printf("%lld\n",k);for(int i=1;i<=k;i++)printf("60 ");return 0;}
for(int i=1;i<=30;i++)ord[i]=i+30;
while(1){
static ll dp[65];memset(dp,0,sizeof(dp));
int lim=rng()%29+1,n=0;dp[0]=1;
while(n<=60){
n++;a[n]=rng()%lim+1;
if(dp[60]+dp[60-a[n]]>k){--n;break;}
for(int i=60;i>=a[n];i--)dp[i]+=dp[i-a[n]];
}ll rk=k-dp[60];
sort(ord+1,ord+31,[&](int x,int y){return dp[60-x]>dp[60-y];});
for(int i=1;i<=30;i++)while(n<60&&rk>=dp[60-ord[i]])a[++n]=ord[i],rk-=dp[60-ord[i]];
if(!rk){
printf("%d\n",n);
for(int i=1;i<=n;i++)printf("%d%c",a[i]," \n"[i==n]);
return 0;
}
}
return 0;
}
标签:int,Bundles,60,序列,Game,1854E,ord,dp,rk
From: https://www.cnblogs.com/tzcwk/p/Codeforces-1854E.html