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

浙公网安备 33010602011771号