把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

luogu P6775 [NOI2020] 制作菜品

题面传送门
首先如果\(m\leq n\)那么一直用最大的食材搞就可以了,显然最大的食材一定大于\(k\)
然后\(m=n-1\)怎么搞
\(d\)排序,然后显然可以用最小的和最大的来凑出\(k\),显然成立。
这样子每次\(n\)减一,\(m\)减一直到\(n=2\)\(m=1\)就可以随便构造了。
但是数据范围还有个\(m=n-2\)
想到把\(d\)分成两个集合且两个集合都满足\(\sum\limits{d_i}=(m-1)k\)
这个显然可以\(O(n^3k)\)的背包来作:设\(f_{i,j,k}\)表示到第\(i\)个,选了\(j\)个且总和为\(k\),用bitset优化可以达到\(85\)分。
然后发现我们只需要让这些\(d\)平均数为\(k\),想到平均数的一个套路就是先减去\(k\),那么\(j\)这一维可以省略,就可以做到\(O(T\frac{n^2k}{w})\)
code:

#include<cstdio>
#include<bitset>
#include<algorithm>
#include<iostream>
#include<cstring>
#define I inline
#define N 509
#define W 5009
using namespace std;
int n,head,m,k,x,y,z,T,flag[N],now;
struct ques{int w,id;}s[N],g[N];
I bool cmp(ques x,ques y){return x.w<y.w;}
I void swap(ques &x,ques &y){ques w=x;x=y;y=w;}
I void solve(){
	register int i,j,h;sort(s+1,s+n+1,cmp);
	while(m>n-1){
		s[n].w-=k;printf("%d %d\n",s[n].id,k);
		for(i=n;i;i--) if(s[i].w<s[i-1].w) swap(s[i],s[i-1]);m--;
	}
	for(i=1;i<=n-2;i++){
		if(s[i].w^k){
			s[n].w-=k-s[i].w;printf("%d %d ",s[n].id,k-s[i].w);
			for(j=n;j>i+1;j--) if(s[j].w<s[j-1].w) swap(s[j],s[j-1]);
		}
		printf("%d %d\n",s[i].id,s[i].w);s[i].w=0;
	}
	if(m){
	    if(s[n-1].w) printf("%d %d ",s[n-1].id,s[n-1].w);
    	printf("%d %d\n",s[n].id,s[n].w);
	}
}
bitset<W*N*2> f[N];
int main(){
	freopen("1.in","r",stdin);
	register int i;scanf("%d",&T);
	while(T--){
		scanf("%d%d%d",&n,&m,&k);memset(flag,0,sizeof(flag)); 
		for(i=1;i<=n;i++) scanf("%d",&s[i].w),s[i].id=i;
		if(m>=n-1)solve();
		else{
			for(i=1;i<=n;i++) g[i]=s[i];f[0][W*N]=1;
			for(i=1;i<=n;i++)f[i]=(g[i].w>k?(f[i-1]|f[i-1]<<g[i].w-k):(f[i-1]|(f[i-1]>>(k-g[i].w))));
			if(!f[n][W*N-k]){printf("-1\n");continue;}now=W*N-k;
			for(i=n;i;i--) !f[i-1][now]&&(now-=g[i].w-k,flag[i]=1);head=n;
			for(n=0,i=1;i<=head;i++) flag[i]&&(s[++n]=g[i],0);m=n-1;solve();
			for(n=0,i=1;i<=head;i++) !flag[i]&&(s[++n]=g[i],0);m=n-1;solve();
		}
	}
}
posted @ 2021-04-13 20:34  275307894a  阅读(54)  评论(0编辑  收藏  举报
浏览器标题切换
浏览器标题切换end