[NOI2020]制作菜品

Posted StaroForgin

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[NOI2020]制作菜品相关的知识,希望对你有一定的参考价值。

制作菜品

题解

首先我们要关注到一个非常重要的条件, m ⩾ n − 2 m\\geqslant n-2 mn2
好吧,我也不知道如何阐述这个条件的重要性,不过 我们只需要考虑这个范围内的 m m m
我们先看看那个 m = n − 1 m=n-1 m=n1的部分分,看看这个部分分有没有什么特殊的解法。
假设把所有菜的量从小到大排序为 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+dnK,否则 d 2 , . . . , d n − 1 d_2,...,d_n-1 d2,...,dn1也是小于 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=n1的问题,考虑 m ⩾ n − 1 m\\geqslant n-1 mn1的部分是否能用类似额方法解决。
显然,此时势必存在一个 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=n1。或者所有的 d i d_i di都等于 K K K,直接一一匹配即可。

那么我们 m = n − 2 m=n-2 m=n2的部分该怎么解决呢?
不妨猜一猜,如果我们能将其拆分成两个 m = n − 1 m=n-1 m=n1的子问题就有解,否则无解。
其必要性是肯定成立的,毕竟拆分后就能按照我们上面的思路进行求解。
至于充分性,我们可以这样想,如果一份菜用了两份食材,就在这两份食材间连边。
显然,我们最多只有 m = n − 2 m=n-2 m=n2条边,最少也会形成两个连通块,只有当这两个连通块构成 m = n − 1 m=n-1 m=n1的子问题时才有解。
这就很好的证明了我们的问题。

那么我们要怎么去找这个拆分呢?
由于要满足条件 ∑ i = 1 n = ( n − 1 ) K \\sum_i=1^n=(n-1)K i=1n=(n1)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]美食节

bzoj2879[Noi2012]美食节 费用流+动态加边

第十五天

bzoj 2875 [Noi2012]随机数生成器 矩阵乘法

[BZOJ2879] [Noi2012] 美食节 (费用流 & 动态加边)

noi 2.6_9272偶数个数字3(DP)