[关闭]
@xiaoziyao 2021-04-20T12:47:57.000000Z 字数 1676 阅读 951

P7519 [省选联考 2021 A/B 卷] 滚榜解题报告

解题报告


P7519 [省选联考 2021 A/B 卷] 滚榜解题报告:

更好的阅读体验

题意

给定支队伍,每支队伍有权值。给定,将分成若干个数之和,按不降顺序分配给个队伍,已知每次分配都会让分配给的队伍权值变为最大(权值相同比较编号大小,小的优先),求分配方案数。

分析

很简单的状压dp+费用提前,但考场降智了,没想到。

为当前分配完的队伍集合为,上一个分配的队伍为,一共消耗的权值大小为的方案数,可是由于分配权值的顺序必须不降,所以还得带上另一个参数来表示上一次分配的权值,可是这样就是的了,比阶乘还劣。

考虑表示出或消除这个参数,根据上面的定义表示出这个参数比较难,因此我们想一想如何将这个参数的费用消除掉。

我们先想一想当我们确定了分配顺序应该怎么判断:我们按照顺序给每个队伍分配最少的权值,最后如果分配失败就说明方案失败,否则可以直接把所有剩下权值分配给最后一个队伍。

我们考虑剩下的权值不分配给最后一个队伍的情况,这时可以对于一个队伍后缀增加一个不递减的权值序列,而对这个序列差分后可以知道这个权值序列可以分成若干次给某个后缀加上相同的权值。

这提醒了我们使用费用提前,转移到(其中集合仅含)时,不难发现需要给队伍增加权值(设一开始权值最大的队伍为),而根据上面的思考可以知道这个权值在以后每个队伍都需要进行分配,那么我们直接将权值乘,提前计算费用即可。

同理,对转移到,我们对于当前队伍需要增加的权值,我们将权值乘就可以消除后续的贡献。

时间复杂度:,用来枚举可以卡卡常。

代码

  1. #include<stdio.h>
  2. #define lowbit(x) x&-x
  3. const int maxn=13,maxm=505,maxk=(1<<maxn);
  4. int n,m,k,maxx,maxp;
  5. int a[maxn+1],tot[maxk],p[maxk];
  6. long long ans;
  7. long long f[maxk][maxn+1][maxm];
  8. inline int max(int a ,int b){
  9. return a>b? a:b;
  10. }
  11. int main(){
  12. scanf("%d%d",&n,&m);
  13. for(int i=1;i<=n;i++){
  14. scanf("%d",&a[i]),p[1<<(i-1)]=i;
  15. if(a[i]>maxx)
  16. maxx=a[i],maxp=i;
  17. }
  18. k=(1<<n)-1,tot[0]=0;
  19. for(int i=1;i<=k;i++)
  20. tot[i]=tot[i>>1]+(i&1);
  21. for(int i=1;i<=n;i++){
  22. int cost=n*(maxx-a[i]+(maxp<i));
  23. if(cost<=m)
  24. f[1<<(i-1)][i][cost]=1;
  25. }
  26. for(int i=1;i<k;i++)
  27. for(int j=i;j;j-=lowbit(j))
  28. for(int s=0;s<=m;s++){
  29. int v=lowbit(j),now=p[v];
  30. if(f[i][now][s]==0)
  31. continue;
  32. for(int t=1;t<=n;t++)
  33. if(((i>>(t-1))&1)==0){
  34. int cost=s+(n-tot[i])*max(a[now]-a[t]+(now<t),0);
  35. if(cost<=m)
  36. f[i|(1<<(t-1))][t][cost]+=f[i][now][s];
  37. }
  38. }
  39. for(int i=0;i<=n;i++)
  40. for(int j=1;j<=m;j++)
  41. ans+=f[k][i][j];
  42. printf("%lld\n",ans);
  43. return 0;
  44. }

省选联考A卷全部题解可见:2021省选联考A卷解题报告

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注