题目
思路
吐槽:何必把 n ⩽ m + 2 n\leqslant m+2 n⩽m+2 的约束藏起来呢?我看到了之后还是不会做啊 😫
这个 + 2 +2 +2 看上去不像好人,先抹杀掉,找找规律。
n ⩽ m n\leqslant m n⩽m
必然存在 d x ⩾ k d_x\geqslant k dx⩾k 。求出 min { d i } ⩾ k \min\{d_i\}\geqslant k min{di}⩾k 的答案平凡。而 min { d i } < k \min\{d_i\}<k min{di}<k 时,将其与 d x ⩾ k d_x\geqslant k dx⩾k 凑出一道菜,递归至 n ′ ⩽ m ′ n'\leqslant m' n′⩽m′ 。可见其必有解。
n = m + 1 n=m+1 n=m+1
不会了。看神的博客吧。原来也可以递归啊,我是伞兵 😅
当 n = 2 n=2 n=2 时有解。重要的递归出口啊,我都没想过 😢
当
n
>
2
n>2
n>2 时显然
min
{
d
i
}
<
k
\min\{d_i\}<k
min{di}<k,并且
min
{
d
i
}
+
max
{
d
i
}
>
k
\min\{d_i\}+\max\{d_i\}>k
min{di}+max{di}>k 。后者于我而言并不显然。它事实上是
m
k
=
∑
d
i
⩽
min
{
d
i
}
+
(
n
−
1
)
max
{
d
i
}
mk=\sum d_i\leqslant \min\{d_i\}+(n{-}1)\max\{d_i\}
mk=∑di⩽min{di}+(n−1)max{di} 移项得到的
min
{
d
i
}
+
max
{
d
i
}
⩾
k
+
(
m
−
1
)
(
k
−
max
{
d
i
}
)
\min\{d_i\}+\max\{d_i\}\geqslant k+(m{-}1)(k-\max\{d_i\})
min{di}+max{di}⩾k+(m−1)(k−max{di})
若
max
{
d
i
}
⩾
k
\max\{d_i\}\geqslant k
max{di}⩾k 则原不等式成立,否则由上式知其仍成立。这真的很容易发现么 😭
因此我们可以将 min { d i } \min\{d_i\} min{di} 和 max { d i } \max\{d_i\} max{di} 做一道菜,递归到 n ′ = m ′ + 1 n'=m'+1 n′=m′+1 。可见其必有解。
n = m + 2 n=m+2 n=m+2
得想办法将这种情况向 n = m + 1 n=m+1 n=m+1 的情况上靠。
由于做菜类似匹配,可以想到建边。因此 m = n − 2 m=n-2 m=n−2 时必然存在 m = n − 1 m=n-1 m=n−1 的连通块,并且恰有两个。只需要确定这两个连通块,内部就分别有解了。
这等价于选出集合 S S S 使得 ∑ i ∈ S d i = k ⋅ ( ∣ S ∣ − 1 ) \sum_{i\in S}d_i=k\cdot (|S|{-}1) ∑i∈Sdi=k⋅(∣S∣−1),或者变形一下就是 ∑ i ∈ S ( d i − k ) = − k \sum_{i\in S}(d_i{-}k)=-k ∑i∈S(di−k)=−k 。这就是个背包问题。
背包大小 O ( n k ) \mathcal O(nk) O(nk),由于只存 b o o l \tt bool bool 值可用 b i t s e t \tt bitset bitset 优化,总复杂度 O ( n 2 k ω ) \mathcal O(\frac{n^2k}{\omega}) O(ωn2k) 。
当然我们同时注意到 ∑ i = 1 n ( d i − k ) = − 2 k \sum_{i=1}^{n}(d_i{-}k)=-2k ∑i=1n(di−k)=−2k,这说明选出来的子集 S S S 的和与补集 ∁ S \complement S ∁S 的和相等;那我们不妨以此为判据,或许好写一点。
代码
#include <cstdio> // Almighty OUYE yyds!!!
#include <algorithm>
#include <cstring>
#include <cctype> // isdigit
#include <vector>
#include <bitset>
#include <set>
#include <random>
using llong = long long;
# define rep(i,a,b) for(int i=(a); i<=(b); ++i)
# define drep(i,a,b) for(int i=(a); i>=(b); --i)
# define rep0(i,a,b) for(int i=(a); i!=(b); ++i)
inline int readint(){
int a = 0, c = getchar(), f = 1;
for(; !isdigit(c); c=getchar()) if(c == '-') f = -f;
for(; isdigit(c); c=getchar()) a = a*10+(c^48);
return a*f;}
const int MAXN = 500;
struct Dish{
int val, id;
bool operator < (const Dish &t) const {
return val == t.val ? id < t.id : val < t.val;
}
};
Dish d[MAXN];
struct Scheme { int a, b, c, d; };
std::vector<Scheme> ans; int k;
inline void makeMeal(Dish &a, Dish &b){
ans.push_back(Scheme{a.id,a.val,b.id,k-a.val});
b.val -= k-a.val;
}
inline void makeMeal(Dish &a){
ans.push_back(Scheme{a.id,k,0,0});
a.val -= k;
}
void _solve_main(std::set<Dish> &shit){
while(!shit.empty()){
Dish a = *shit.begin();
shit.erase(shit.begin());
if(a.val >= k){ // do it alone
makeMeal(a);
if(a.val) shit.insert(a);
continue;
}
Dish b = *shit.rbegin();
shit.erase(b);
makeMeal(a,b);
if(b.val) shit.insert(b);
}
}
void solve1(int n){
static std::set<Dish> shit;
rep0(i,0,n) shit.insert(d[i]);
_solve_main(shit);
}
void solve1(const std::vector<int> &buc){
static std::set<Dish> shit;
for(const int &x : buc) shit.insert(d[x]);
_solve_main(shit);
}
const int PACK = MAXN*5000;
std::bitset<PACK> dp[MAXN+1];
std::vector<int> buc[2];
void trace_back(int x, int v){
for(int opt=0; (--x)!=-1; ){ // step-back
const int me = d[x].val-k; // package weight
if(0 <= v+me && v+me < PACK && dp[x].test(v+me))
v += me, buc[opt].push_back(x); // dec
else if(me <= v && v-me <= PACK && dp[x].test(v-me))
v -= me, buc[opt^1].push_back(x); // inc
else if(me > 0) v = me-v, buc[opt^=1].push_back(x);
else v = -me-v, buc[opt].push_back(x), opt ^= 1;
}
}
int main(){
std::mt19937 rnd; rnd.seed(114514);
for(int T=readint(); T; --T){
int n = readint(), m = readint();
k = readint(); // global
rep0(i,0,n) d[i].val = readint(), d[i].id = i+1;
ans.clear(), ans.reserve(m);
if(n <= m+1) solve1(n);
else{ // package problem
dp[0].reset(), dp[0].set(0);
std::shuffle(d,d+n,rnd);
rep0(i,0,n){
int me = d[i].val-k; // package weight
if(me < 0) me = -me; // the same behaviour
dp[i+1] = (dp[i]<<me)|(dp[i]>>me);
rep0(j,0,me) if(dp[i].test(j))
dp[i+1].set(me-j); // negate
}
if(!dp[n].test(0)){ puts("-1"); continue; }
buc[0].clear(), buc[1].clear(), trace_back(n,0);
solve1(buc[0]), solve1(buc[1]);
}
for(const Scheme &xjx : ans){
printf("%d %d",xjx.a,xjx.b);
if(xjx.d) printf(" %d %d",xjx.c,xjx.d);
putchar('\n');
}
}
return 0;
}
后记
我遇到了一个 C++
语言本身的问题:如果使用
using PII = std::pair<int,int>;
struct mystruct : public PII{
int &a, &b;
mystruct(): PII(), a(first), b(second) { }
};
编译可以通过,但是 a
的值会和 first
不一样!
我不知道这是为什么。上 Stackoverflow \text{Stackoverflow} Stackoverflow 都没查到。