P6775 「NOI2020」制作菜品

非常好的 训练。

题目简介

题目名称:制作菜品
题目来源:

评测链接:https://www.luogu.com.cn/problem/P6775

形式化题意:有 种颜色的小球,颜色 有 个,一共有 个坑,每个坑能放 个小球。询问是否存在一种放球方案使得每个坑里的球至多有 种颜色。求出方案或报告无解。多测。

数据范围:

你发现数据点分为了三种部分:,所以我们也按照这个情况来进行讨论。

首先考虑 的情况,我们将 排序,因为有 ,所以如果存在一个 ,那么就一定存在一个 。

所以我们考虑贪心选择,首先填入 ,然后在坑中填入最大的 的一部分,然后 后重新排序。以此做 次就可以得到答案。

时间复杂度 ,可以通过 。

然后你发现这个方法在 时适用,因为每次操作会减少恰好一个颜色的小球,那么 次之后剩下的小球就是原本个数最多的,用于填补前 个剩下的部分。时刻注意 的条件,所以保证了任何情况下 都不成立。

但是你发现这个方法无法处理 的情况,因为这种情况会剩下两个颜色的小球,也就是在某一操作时,出现了 ,也就是剩下 个坑的时候 种球两两组合。然后你发现也只有在 的时候才有可能出现无解的情况。也就是剩下 个坑时剩下的 种球无法两两组合。

但上面的做法实在是太优美了,所以我们考虑把 向 转移。

我们找到 的一个真子集 ,那么我们有 ,也就是 ,所以,我们可以通过这种方式将 拆成两个 的子问题。

然后考虑怎么样找到这个 ,根据上面的性质,我们知道:

那么两边同时减去 得到:

那么这就是一个 可行性背包的问题,每个物品的体积为 ,背包大小为 ,求出恰好装满背包的方案。然后我们记录一下方案后按照 做两次就行了。

由于体积和容量涉及负数,所以我们开始的时候平移一下即可。

时间复杂度 。可以通过 ,怎么秽蚀呢?

然后考虑可行性背包的本质,我们将当前可行性状态记为 ,那么加入一个体积为 的物品,会产生新的状态为 ,实际上在二进制压缩中为 t|(t<<v)。也就是一次平移的问题。

所以经典优化,将 数组用 std::bitset 优化,执行左移右移。最后时间复杂度 。可以通过本题。

AC Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
// ----- Eternally question-----
// Problem: P6775 [NOI2020] 制作菜品
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P6775
// Memory Limit: 512 MB
// Time Limit: 2000 ms
// Written by: Eternity
// Time: 2023-10-06 08:11:21
// ----- Endless solution-------

#include<bits/stdc++.h>
#define re register
typedef long long ll;
template<class T>
inline void read(T &x)
{
x=0;
char ch=getchar(),t=0;
while(ch<'0'||ch>'9') t|=ch=='-',ch=getchar();
while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
if(t) x=-x;
}
template<class T,class ...T1>
inline void read(T &x,T1 &...x1){ read(x),read(x1...); }
template<class T>
inline void write(T x)
{
if(x<0) putchar('-'),x=-x;
if(x>9) write(x/10);
putchar(x%10+'0');
}
template<>
inline void write(bool x){ putchar(x?'1':'0'); }
template<>
inline void write(char c){ putchar(c); }
template<>
inline void write(char *s){ while(*s!='\0') putchar(*s++); }
template<>
inline void write(const char *s){ while(*s!='\0') putchar(*s++); }
template<class T,class ...T1>
inline void write(T x,T1 ...x1){ write(x),write(x1...); }
template<class T>
inline bool checkMax(T &x,T y){ return x<y?x=y,1:0; }
template<class T>
inline bool checkMin(T &x,T y){ return x>y?x=y,1:0; }
using Pir=std::pair<int,int>;
#define fir first
#define sec second
#define Mkr std::make_pair
const int MAXN=5e2+10,MAXM=5e3+10,MAXK=5e3+10,MAXS=5e6+10;
const int S=2.5e6;
int Test,N,M,K,D[MAXN];
inline void solve(std::vector<Pir>vec,int m)
{
int Len=vec.size();
for(int i=1;i<=m;++i)
{
std::sort(vec.begin(),vec.end());
int pos=0;
for(;pos<Len;++pos) if(vec[pos].fir) break;
if(vec[pos].fir>=K)
{
write(vec[pos].sec,' ',K,'\n');
vec[pos].fir-=K;
}
else
{
vec[Len-1].fir-=K-vec[pos].fir;
write(vec[pos].sec,' ',vec[pos].fir,' ');
write(vec[Len-1].sec,' ',K-vec[pos].fir,'\n');
vec[pos].fir=0;
}
}
}
std::bitset<MAXS>Dp[MAXN];
int val[MAXN];
inline bool get(std::vector<Pir>&vec1,std::vector<Pir>&vec2)
{
std::memset(Dp,0,sizeof(Dp));
for(int i=1;i<=N;++i) val[i]=D[i]-K;
Dp[0][S]=1;
for(int i=1;i<=N;++i)
{
if(val[i]>=0) Dp[i]|=(Dp[i-1]<<val[i])|Dp[i-1];
else Dp[i]|=(Dp[i-1]>>(-val[i]))|Dp[i-1];
}
if(Dp[N][S-K]==0) return 0;
vec1.clear(),vec2.clear();
int pos=N,posv=S-K;
while(pos)
{
if(Dp[pos-1][posv-val[pos]])
{
vec1.push_back(Mkr(D[pos],pos));
posv-=val[pos];
}
else vec2.push_back(Mkr(D[pos],pos));
--pos;
}
return 1;
}
int main()
{
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
read(Test);
while(Test--)
{
read(N,M,K);
for(int i=1;i<=N;++i) read(D[i]);
if(M>=N-1)
{
std::vector<Pir>vec;
for(int i=1;i<=N;++i) vec.push_back(Mkr(D[i],i));
solve(vec,M);
}
else
{
std::vector<Pir>vec1,vec2;
bool ok=get(vec1,vec2);
if(!ok) puts("-1");
else solve(vec1,vec1.size()-1),solve(vec2,vec2.size()-1);
}
}
return 0;
}
/*

*/