[NOI2020]制作菜品
Posted StaroForgin
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[NOI2020]制作菜品相关的知识,希望对你有一定的参考价值。
制作菜品
题解
首先我们要关注到一个非常重要的条件,
m
⩾
n
−
2
m\\geqslant n-2
m⩾n−2。
好吧,我也不知道如何阐述这个条件的重要性,不过 我们只需要考虑这个范围内的
m
m
m。
我们先看看那个
m
=
n
−
1
m=n-1
m=n−1的部分分,看看这个部分分有没有什么特殊的解法。
假设把所有菜的量从小到大排序为
d
1
,
d
2
,
.
.
.
,
d
n
d_1,d_2,...,d_n
d1,d2,...,dn,观察一下这个数列会有什么样的性质:
- d 1 < K d_1<K d1<K,否则 ∑ d > m K \\sum d>mK ∑d>mK,显然不行。
- d 1 + d n ⩾ K d_1+d_n\\geqslant K d1+dn⩾K,否则 d 2 , . . . , d n − 1 d_2,...,d_n-1 d2,...,dn−1也是小于 K K K的,加起来不可能达到 m K mK mK。
有了上面的结论,我们就可以想想做法了。
显然,我们每次可以从将
d
1
d_1
d1全部去掉,再从
d
n
d_n
dn中去掉不够的部分。
这样,就能让我们的
n
,
m
n,m
n,m都减
1
1
1直到
n
=
2
n=2
n=2。
而
n
=
2
n=2
n=2时直接全选
d
1
+
d
2
d_1+d_2
d1+d2就可以得到
K
K
K了。
于是,我们顺利地解决了
m
=
n
−
1
m=n-1
m=n−1的问题,考虑
m
⩾
n
−
1
m\\geqslant n-1
m⩾n−1的部分是否能用类似额方法解决。
显然,此时势必存在一个
d
i
>
K
d_i> K
di>K,我们可以考虑从
d
i
d_i
di中去掉一个
K
K
K,使得
m
m
m减一,直到
m
=
n
−
1
m=n-1
m=n−1。或者所有的
d
i
d_i
di都等于
K
K
K,直接一一匹配即可。
那么我们
m
=
n
−
2
m=n-2
m=n−2的部分该怎么解决呢?
不妨猜一猜,如果我们能将其拆分成两个
m
=
n
−
1
m=n-1
m=n−1的子问题就有解,否则无解。
其必要性是肯定成立的,毕竟拆分后就能按照我们上面的思路进行求解。
至于充分性,我们可以这样想,如果一份菜用了两份食材,就在这两份食材间连边。
显然,我们最多只有
m
=
n
−
2
m=n-2
m=n−2条边,最少也会形成两个连通块,只有当这两个连通块构成
m
=
n
−
1
m=n-1
m=n−1的子问题时才有解。
这就很好的证明了我们的问题。
那么我们要怎么去找这个拆分呢?
由于要满足条件
∑
i
=
1
n
=
(
n
−
1
)
K
\\sum_i=1^n=(n-1)K
∑i=1n=(n−1)K,将右边的减过去,也就构成了一个背包匹配问题,除了第一个外没多选一个就加上它的权值减
K
K
K,直到权值和为
0
0
0。
我们看看有没有让背包总和为
0
0
0的解即可。
当然,直接做背包可能会
T
T
T,可以用
bitset
\\textbitset
bitset进行优化,毕竟都是
b
o
o
l
bool
bool变量。
时间复杂度 O ( T ( n log n + n 2 K ω ) ) O\\left(T(n\\log n+\\fracn^2K\\omega)\\right) O(T(nlogn+ωn2K))。
源码
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int,int> pii;
#define MAXN 1005
#define MAXM 1250005
#define pb push_back
#define mkpr make_pair
#define fir first
#define sec second
#define lowbit(x) (x&-x)
const int mo=1e9+7;
const int jzm=23;
const int zero=200000;
template<typename _T>
_T Fabs(_T x)return x<0?-x:x;
template<typename _T>
void read(_T &x)
_T f=1;x=0;char s=getchar();
while(s<'0'||s>'9')if(s=='-')f=-1;s=getchar();
while('0'<=s&&s<='9')x=(x<<3)+(x<<1)+(s^48);s=getchar();
x*=f;
int add(int x,int y,int p)return x+y<p?x+y:x+y-p;
void Add(int &x,int y,int p)x=add(x,y,p);
int qkpow(int a,int s,int p)int t=1;while(s)if(s&1)t=1ll*a*t%p;a=1ll*a*a%p;s>>=1;return t;
int T,n,m,K,tota,totb,val[505],a[505],b[505],ord[505],idx;
bitset<MAXM>st[505];
bool cmp(int x,int y)return val[x]>val[y];
priority_queue<pii>q1;
priority_queue<pii,vector<pii>,greater<pii> >q2;
vector<pii>ans[5005];
void sakura(int len,int *d)
while(!q1.empty())q1.pop();for(int i=1;i<=len;i++)q1.push(mkpr(val[d[i]],d[i]));
while(!q2.empty())q2.pop();for(int i=1;i<=len;i++)q2.push(mkpr(val[d[i]],d[i]));
while(!q1.empty()&&!q2.empty())
while(!q1.empty())if(val[q1.top().sec]!=q1.top().fir)q1.pop();else break;
while(!q2.empty())if(val[q2.top().sec]!=q2.top().fir)q2.pop();else break;
if(q1.empty()||q2.empty())break;idx++;
pii t1=q1.top(),t2=q2.top();int x=t1.sec,y=t2.sec;
ans[idx].pb(mkpr(y,val[y]));ans[idx].pb(mkpr(x,K-val[y]));
val[x]-=K-val[y];val[y]=0;q1.pop();q2.pop();
if(val[x])q1.pushBZOJ2879: [Noi2012]美食节
bzoj 2875 [Noi2012]随机数生成器 矩阵乘法