题解:luogu P6775([NOI2020] 制作菜品)

1. Solution

首先有一个结论:

Theory 1

\(m=n-1\) 的时候,一定存在合法方案。

proof

我们不妨将所有 \(d_i\) 从小到大排序,显然不影响合法方案是否存在。
\(n=2,m=1\) 的时候显然成立,现在我们考虑将这个结论推广。

Lemma 1.1

\(d_1<k\)

proof

如果 \(d_1\ge k\),则 \(\sum_{i=1}^n d_i\ge nd_1\ge nk>(n-1)k\),和条件不符。

Lemma 1.2

\(\forall n>2,d_1+d_n>k\)

proof

如果 \(d_1+d_n\le k\),则 \(d_n\le k-d_1\le k-1\),所以 \(\sum_{i=2}^n d_i\le (n-1)(k-d_1)=(n-1)k-(n-1)d_1\),所以 \(\sum_{i=1}^n d_i\le (n-1)k-(n-2)d_1< (n-1)k\),与条件不符。

所以,设 \(n\le k\) 时命题成立,证明 \(n=k+1\) 时成立,我们通过耗尽第一种原料并加入最后一种原料,可以转换为 \(n=k\) 的命题,根据假设,是成立的。
根据数学归纳法,得到 \(n\ge 2\) 时命题成立。
根据上面的做法模拟,利用 set 维护当前序列,可以做到 \(\text{O}(n\log n)\) 的时间复杂度。

然后,我们发现另一个结论:

Theory 2

\(m\ge n\) 的时候,一定存在合法方案。

proof

同样将所有 \(d_i\) 从小到大排序,显然不影响合法方案是否存在。
我们考虑将问题转换为上面的形式,也即 \(m=n-1\) 的情况。

Lemma 2.1

\(d_n\ge k\)

proof

显然成立,若 \(d_n<k\),则 \(\sum_{i=1}^n d_i\le nd_n<nk\),与条件冲突。

根据这个引理,我们可以取第 \(n\) 种原材料 \(k\) 克,直接做一道菜,此时 \(m\) 减一,\(n\) 至多减一,不停做,直到 \(n=0\)\(m=n-1\) 即可。

Theory 3

\(m=n-2\) 的时候,存在合法方案,当且仅当所有原材料可以被划分为两组,且每一组满足 \(n=|S|,\sum_{i\in S} d_i=(n-1)k\)

proof

显然,直接合并两组的方案即可。

由此,我们对所有 \(d_i-k\) 做零一背包,则条件就是存在和为 \(-k\) 的方案,利用 bitset 优化即可做到 \(\text{O}(\frac{n^2k}{\omega})\)

2. Code

/*by ChenMuJiu*/
/*略去缺省源和快读快写*/
const int N=505;
int n,m,k;
int d[N],idx[N];
namespace Subtask1{
#define pii pair<int,int>
set<pii>st;
void solve(int *d,int *idx,int n,int m){
	st.clear();
	for(int i=1;i<=n;i++)
		st.insert({d[i],idx[i]});
	while(n&&m>=n){
		auto it=*st.rbegin();
		st.erase(it);
		write(it.second),Spa,write(k),Nxt;
		it.first-=k,m--;
		if(it.first==0){
			n--;
			continue;
		}
		st.insert(it);
	}
	while(st.size()>=2){
		auto x=*st.rbegin(),y=*st.begin();
		st.erase(x),st.erase(y);
		write(y.second),Spa,write(y.first),Spa,write(x.second),Spa,write(k-y.first),Nxt;
		x.first-=k-y.first;
		st.insert(x);
	}
}
#undef pii
}
namespace Subtask2{
const int N=5e6+5;
bitset<N>f[505];
int tmpd[505],tmpidx[505];
void solve(){
	for(int i=0;i<=n;i++)
		f[i].reset();
	f[0][2500000]=1;
	for(int i=1;i<=n;i++){
		int x=d[i]-k;
		f[i]=f[i-1];
		if(x>=0)
			f[i]|=(f[i-1]<<x);
		else
			f[i]|=(f[i-1]>>-x);
	}
	if(!f[n][2500000-k]){
		puts("-1");
		return ;
	}
	vector<int>a,b;
	for(int i=n,S=2500000-k;i>=1;i--){
		if(f[i-1][S-(d[i]-k)]){
			a.push_back(i);
			S-=d[i]-k;
		}else b.push_back(i);
	}
	int cnt=0;
	for(auto tmp:a){
		cnt++;
		tmpd[cnt]=d[tmp];
		tmpidx[cnt]=tmp;
	}
	Subtask1::solve(tmpd,tmpidx,cnt,cnt-1);
	cnt=0;
	for(auto tmp:b){
		cnt++;
		tmpd[cnt]=d[tmp];
		tmpidx[cnt]=tmp;
	}
	Subtask1::solve(tmpd,tmpidx,cnt,cnt-1);
}
}
signed main(){
	int t;
	read(t);
	while(t--){
		read(n),read(m),read(k);
		for(int i=1;i<=n;i++)
			read(d[i]),idx[i]=i;
		if(m>=n-1)
			Subtask1::solve(d,idx,n,m);
		else Subtask2::solve();
	}
}
posted @ 2026-05-14 14:08  陈牧九  阅读(3)  评论(0)    收藏  举报