题目
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
",ans);
return 0;
}