题解[[2021省选联考] 滚榜]

题目

Luogu

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;
}

完结撒花❀

posted @ 2021-05-12 22:22  xxbbkk  阅读(97)  评论(0编辑  收藏  举报