LOJ#3342.【NOI2020】制作菜品(dish)

Description

LOJ3342
在这里插入图片描述

  • n ≤ 500 , m ≥ n − 2 , k ≤ 5000 n\le500,m\ge n-2,k\le5000 n500,mn2,k5000

Solution

  • 一道性质题。
  • 考场上想了一会儿的网络流,但是并不可做,之后尝试猜结论,打了一个暴力。
  • 之后看到了 m = n − 1 m=n-1 m=n1的部分分,虽然想到了树,以及每一次的叶子都是剩余值最小的那个,但是并不知道到它和哪一个合并在一起,然后就没有想法了。
  • 实际上,对于这一类的题目,应该大胆顺着自己的结论往下走,多玩一玩样例,就可以发现只要 m ≥ n − 1 m\ge n-1 mn1就一定有解,再进行一定的推导就可以发现最小的那个叶子不管谁是它的父亲都可以。
  • 考虑证明 m = n − 1 m=n-1 m=n1的任意情况都有解:设 x ( x ≥ 0 ) x(x\ge0) x(x0)为当前剩下的最小的,如果没有一个 y y y使得 x + y > = k x+y>=k x+y>=k的话,那么 s u m m a x < x + ( n − 1 ) ( k − x ) ≤ ( n − 1 ) k sum_{max}<x+(n-1)(k-x)\le (n-1)k summax<x+(n1)(kx)(n1)k,与 s u m = ( n − 1 ) k sum=(n-1)k sum=(n1)k矛盾,那么当前选择一个可以的 y y y x x x匹配即可,剩下的子问题依旧有解。
  • 那么不难发现 m ≥ n m\ge n mn,一定有 a i ≥ k a_i\ge k aik,直接取出来即可转化为 m = n − 1 m=n-1 m=n1的情况。
  • 考虑 m = n − 2 m=n-2 m=n2,那么如果最后的边连出来,至少有两个联通块,所以只要分成两个集合,每一个集合 ∣ S ∣ |S| S都是 ( ∣ S ∣ − 1 ) k (|S|-1)k (S1)k就可以分别求解。
  • 接下来用一个暴力的bitset优化背包即可。
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<set>
#include<bitset>
#define maxn 505
#define maxm 2000005
using namespace std;

int T,n,m,K,i,j,k,M,a[maxn],d[maxn],sum[maxn],bz[maxn];
struct arr{
    
    int x,i;arr(int _x=0,int _i=0){
    
    x=_x,i=_i;}} tmp;
int operator<(arr a,arr b){
    
    return a.x>b.x||a.x==b.x&&a.i<b.i;}
set<arr> s;
set<arr> ::iterator it;
bitset<maxm*2> f[maxn];

void doit(int m){
    
    
	s.clear();
	for(i=1;i<=d[0];i++) s.insert(arr(a[d[i]],d[i]));
	while (m>n-1) {
    
    
		it=s.begin(),tmp=*it,s.erase(it);
		printf("%d %d\n",tmp.i,K);
		tmp.x-=K,s.insert(tmp);
		m--;
	}
	while (m--){
    
    
		it=s.end(),it--,tmp=*it,s.erase(it);
		printf("%d %d ",tmp.i,tmp.x);
		if (tmp.x<K){
    
    
			k=K-tmp.x; 
			it=s.begin(),tmp=*it,s.erase(it);
			printf("%d %d ",tmp.i,k);
			tmp.x-=k,s.insert(tmp);
		}
		printf("\n");
	}
}

void dp(){
    
    
	for(i=1;i<=n;i++) sum[i]=sum[i-1]+a[i];
	for(i=0;i<=n;i++) f[i].reset();
	for(i=1;i<=n;i++){
    
    
		if (a[i]-K>=0) f[i]=f[i-1]<<a[i]-K;
		else f[i]=f[i-1]>>K-a[i];
		f[i]|=f[i-1];
		f[i][sum[i-1]-K*(i-1)+M]=1;
	}
	if (f[n][M-K]==0) printf("-1\n"); else {
    
    
		memset(bz,0,sizeof(bz));
		int now=M-K;
		for(i=n;i>=1;i--) {
    
    
			if (f[i-1][now-(a[i]-K)]) bz[i]=1,now-=a[i]-K; else
			if (now==sum[i-1]-K*(i-1)+M){
    
    
				for(j=1;j<i;j++) bz[j]=1;
				now=0;break;
			}
		}
		d[0]=0;for(i=1;i<=n;i++) if (bz[i]) d[++d[0]]=i; 
		doit(d[0]-1);
		d[0]=0;for(i=1;i<=n;i++) if (!bz[i]) d[++d[0]]=i;
		doit(d[0]-1);
	}
}

int main(){
    
    
	freopen("ceshi.in","r",stdin);
	freopen("ceshi.out","w",stdout);
//	freopen("dish.in","r",stdin);
//	freopen("dish.out","w",stdout);
	scanf("%d",&T);
	while (T--){
    
    
		scanf("%d%d%d",&n,&m,&K),M=m*K;
		for(i=1;i<=n;i++) scanf("%d",&a[i]);
		if (m>=n-1) {
    
    
			d[0]=0;for(i=1;i<=n;i++) d[++d[0]]=i;
			doit(m);
		} else
			dp();
	}
}

猜你喜欢

转载自blog.csdn.net/qq_43649416/article/details/108392580