题解[[2021省选联考] 滚榜]
题目
Sol
其实还是很好拿分的,这道题是本蒟蒻在省选中得分最高的一题(\(Day1T1\)想复杂了直接跳过,白丢\(60\)分)。
还是水平不够吧,在本校的成绩不理想。
60
直接\(dfs\) 。
发现我们就是要把\(m\)道题分合理分配,是我们想要的排名出现。
先全排列把所有的排名处理出来。
对于一个排列:
设这个排列为\(p\) 。
那我们肯定想的是前面的队伍尽量少分,以免后面的队伍无法成为新的第一名。
那贪心策略出来啦。
设\(b_{p_{i-1}}\)为我们分给上一个队的过题数,\(num\)为当前第一名(也就是上一个队)的过题数,那我们要分给这一个队的过题数是:
\[max(b_{p_{i-1}},num-a[p[i]]+(p[i]<p[i-1]?0:1))
\]
\(dfs\)一下就好了。
100
\(n<=13\) ,考虑状压。
肯定有一维是\(2^n\)的。
设\(f[S][i][used]\)为 当前集合为\(S\),最后一个元素为\(i\) ,总共用了\(used\)道题的排列数。
转移:
枚举集合\(S\) ,由\(S\)向\(S|(1<<(j-1))\)转移。
费用可行性计算详见代码。
Code
#include<bits/stdc++.h>
#define lb(i) ((i)&(-i))
#define ll long long
using namespace std;
int n,m,AS,pos,a[15],now[8199];
ll ans,f[8199][15][520];
inline int read(){
int w=0;
char ch=getchar();
while(ch>'9'||ch<'0') ch=getchar();
while(ch>='0'&&ch<='9'){
w=(w<<3)+(w<<1)+(ch^48);
ch=getchar();
}
return w;
}
int main(){
n=read(),m=read();
AS=(1<<n)-1,a[0]=-1;
for(int i=1;i<=n;i++){
a[i]=read();
if(a[i]>a[pos]) pos=i;
now[1<<(i-1)]=i;
}
for(int i=1;i<=n;i++){
int nd=n*(a[pos]-a[i]+(pos<i));
//最少要的费用为 (最大ai-第一个ai)*n
//有那么一点费用提前计算的味道
if(nd<=m) f[1<<(i-1)][i][nd]=1;
//可行则赋值
}
for(int S=1;S<AS;S++){
int pct=0;
for(int i=1;i<=n;i++)
if(S&(1<<(i-1))) pct++;
for(int i=S;i;i-=lb(i)){
for(int used=0;used<=m;used++){
int p=now[lb(i)];
for(int j=1;j<=n;j++){
if(!(S&(1<<(j-1)))){
int nd=used+(n-pct)*(max(0,a[p]-a[j]+(p<j)));
//费用为(目前用的+以后最少要用的(当前-上一个)×(后面还有几个))
if(nd<=m) f[S|(1<<(j-1))][j][nd]+=f[S][p][used];
}
}
}
}
}
for(int i=1;i<=n;i++)
for(int j=0;j<=m;j++)
ans+=f[AS][i][j];
printf("%lld\n",ans);
return 0;
}