Luogu6775 [NOI2020] 制作菜品 做题记录

link

主要记录一下做题过程。

首先题目看上去很不好处理,考虑从部分分的角度入手。

先看 m=n1 的部分分,这个性质让我们很容易想到一棵树。考虑把原材料当作点,菜品当作边,一道连接 (x,y) 的菜品表示只能用编号为 xy 的原材料。

对于这棵树,我们每次选择一个叶子,然后处理掉连接叶子的边表示的菜品,并删掉叶子。可以发现,一棵树确定了唯一一种方案。

但是这个方案不一定合法。当叶子原材料数量 >k 或一个点的原材料数量被减到负数时,方案不合法。

我们考虑归纳构造,不断将点数减小。一种很直接的想法是设 x,yd 最大和最小的点,每次在 x,y 之间连一条边,然后保留 x,更新 dxdx+dyk

这是否一定合法?

  • 由于 i=1ndi=mk=(n1)k,得 i=1ndin=n1nk,可知 d<k,那么一定有 dy<k

  • 考虑最坏情况下,除了 dy,其他点的 di 都和 dx 相同,此时 i=1ndi=dy+(n1)dx=(n1)k,可得 dyn1+dx=k,那么 dx+dy>k

所以一定合法,这样我们就做完了 m=n1 的部分分。

考虑 mn1 的部分分,很容易想到转化成 m=n1 的形式。考虑归纳构造,一个直接的想法是,每次选取 d1,d2,...,dn 中最大的点 x,然后直接 dxdxk。再次验证合法性:

  • 考虑此时 mn,且 i=1ndi=mknk,则 dk,显然有 dx=maxdik

注意有个坑点,就是如果 dx=k,那么直接令 nn1 并删掉 dx,官方数据没有卡,但是洛谷有 Hack 数据。

最后考虑 m=n2 的部分,边数竟然比树还少,此时这张图一定有至少两个连通块。可以发现,三个及以上个连通块一定不优,所以我们只需要枚举一个连通块。

此时这两个连通块都是树,如果找出了连通块,可以转成 m=n1 的形式。考虑一个连通块 S 的合法条件,为 iSdi=(|S|1)k,这个显然可以背包解决。

可以把每个 di 减去 k,就不用存 |S| 这一维,剩下直接 bitset 即可。注意此时 mn 同级,时间复杂度 O(n2kω)

点击查看代码
#include <bits/stdc++.h>
#define ll long long
#define ull unsigned ll
#define pir pair <ll, ll>
#define mkp make_pair
#define fi first
#define se second
#define pb push_back
using namespace std;
const ll maxn = 510;
ll t, n, m, k, d[maxn], id1[maxn], id2[maxn], len1, len2;
bitset <5000005> f[maxn];
bool cmp(ll x, ll y) {return d[x] < d[y];}
void sort_insert(ll *id, ll len) {
	ll i, x = id[len];
	for(i = len - 1; i; i--)
		if(d[id[i]] <= d[x]) break;
	for(ll j = len; j > i + 1; j--) id[j] =  id[j - 1];
	id[i + 1] = x;
}
void solve(ll *id, ll len) {
	sort(id + 1, id + 1 + len, cmp);
	while(len > 1) {
		printf("%lld %lld %lld %lld\n", 
			   id[1], d[id[1]], id[len], k - d[id[1]]);
		d[id[len]] += d[id[1]] - k;
		for(ll i = 1; i < len; i++) id[i] = id[i + 1];
		--len, sort_insert(id, len);
	}
}
void scheme(ll i, ll j) {
	if(i == 0) return;
	ll x = d[i] - k;
	if(j - x >= 0 && f[i - 1][j - x]) {
		id1[++len1] = i;
		scheme(i - 1, j - x);
	} else {
		id2[++len2] = i;
		scheme(i - 1, j);
	}
}
int main() {
	scanf("%lld", &t);
	while(t--) {
		scanf("%lld%lld%lld", &n, &m, &k);
		for(ll i = 1; i <= n; i++) scanf("%lld", d + i);
		if(m == n - 2) {
			ll B = m * k;
			f[0].reset(), f[0][B] = 1;
			for(ll i = 1; i <= n; i++) {
				ll x = d[i] - k;
				if(x >= 0) f[i] = f[i - 1] | (f[i - 1] << x);
				else f[i] = f[i - 1] | (f[i - 1] >> (-x));
			}
			if(!f[n][B - k]) {
				puts("-1"); continue;
			} len1 = len2 = 0, scheme(n, B - k);
			solve(id1, len1);
			solve(id2, len2);
		} else {
			for(ll i = 1; i <= n; i++) id1[i] = i;
			sort(id1 + 1, id1 + 1 + n, cmp);
			for(ll i = m; n && i >= n; i--) {
				printf("%lld %lld\n", id1[n], k);
				d[id1[n]] -= k;
				if(d[id1[n]]) sort_insert(id1, n);
				else --n;
			}
			solve(id1, n);
		}
	}
	return 0;
}

出处:https://www.cnblogs.com/Sktn0089/p/18322365

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

posted @   Lgx_Q  阅读(9)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
more_horiz
keyboard_arrow_up dark_mode palette
选择主题
点击右上角即可分享
微信分享提示