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