洛谷 P6775 - [NOI2020] 制作菜品(找性质+bitset 优化 dp)
好久没写过题解了,感觉几天没写手都生疏了
首先这种题目直接做肯定是有些困难的,不过注意到题目中有个奇奇怪怪的条件叫 \(m\ge n-2\),我们不妨从此入手解决这道题。
我们先来探究 \(m=n-1\) 的情况,观察大样例可知这种情况一定有解,我们不妨考虑这样一个贪心:假设 \(x\) 为使 \(d_i\) 取到最小值的 \(i\),\(y\) 为使得 \(d_i\) 取到最大值的 \(i\),那么我们就用 \(d_x\) 个原料 \(x\) 与 \(k-d_x\) 个原料 \(y\) 制作成一个菜品,这样会消耗掉一种菜品(\(x\)),重复 \(n-1\) 轮即可。
为什么这样贪心是对的?我们考虑一次操作,为了证明这次操作是合法的,我们需证明以下两个结论:
- \(d_x<k\)
- \(k-d_x\le d_y\),即 \(d_x+d_y\ge k\)。
这两个结论都可采用反证法证明。关于结论一,假设命题不成立,那么 \(d_x\ge k\),而由 \(d_x\) 的定义知 \(d_i\ge d_x\ge k\),故 \(\sum\limits_{i=1}^nd_i\ge nd_x\ge nk>mk\),矛盾。关于结论二,假设命题不成立,那么 \(d_y<k-d_x\),由 \(d_y\) 的定义知 \(d_i\le d_y<k-d_x\),故 \(\sum\limits_{i=1}^nd_i<(n-1)(k-d_x)+d_x=k+(n-2)(k-d_x)<k+(n-2)k=(n-1)k\),矛盾!
因此每次操作都是合法的。而显然每次操作之后都会有一个原料被消耗尽,因此我们就由 \(n\) 的情况过渡到了 \(n-1\) 的情况。又当 \(n=2\) 时有 \(d_1+d_2=k\),可以直接把两种原料搞在一起,符合条件。由数学归纳法可知这个贪心是没问题的。
接下来再考虑 \(m\ge n\) 的情况,我们考虑将 \(m\ge n\) 的情况向 \(m=n-1\) 的情况过渡,还是设 \(x\) 为使 \(d_i\) 取到最小值的 \(i\),\(y\) 为使得 \(d_i\) 取到最大值的 \(i\),这里有一个显然的结论,那就是 \(d_y\ge k\),否则 \(\sum\limits_{i=1}^nd_i<nk\le mk\),矛盾。因此我们只需每次选取 \(d_i\) 最大的 \(i\) 并消耗掉 \(k\) 克 \(i\) 原料,直到 \(m=n-1\) 为止即可。
最后考虑 \(m=n-2\) 的情况,再次观察样例可知这种情况就不一定有解了,那 \(m=n-2\) 的情况究竟什么时候有解,什么时候无解呢?
又到了考验选手观察能力的时候了,这里还有第四个结论,那就是 \(m=n-2\) 的情况有解当且仅当存在 \(S\subset\{1,2,3,\cdots,n\}\) 满足 \(\sum\limits_{x\in S}d_x=k(|S|-1)\)。
证明:充分性显然,记全集为 \(U=\{1,2,3,\cdots,n\}\),既然 \(S\) 满足 \(\sum\limits_{x\in S}d_x=k(|S|-1)\),那么 \(T=U-S\) 也一定满足 \(\sum\limits_{x\in T}d_x=k(|T|-1)\),因此我们只需对 \(S,T\) 分别执行 \(m=n-1\) 的操作即可。必要性:我们假设对于某个序列 \(d_1,d_2,\cdots,d_n\) 存在符合要求的解,我们考虑对于两个原料 \(i,j\),如果它们曾共同作为原料出现在这 \(n-2\) 个菜品中的某一个中,那么就连一条边 \((i,j)\),特别地如果一种原料单独做成一道菜品就连一条自环。显然这样会得到一张图 \(G=(V,E)\),并且 \(|V|=n,|E|\le n-2\),因此 \(G\) 不连通,而显然 \(G\) 中一定存在一个连通块是一棵树(否则假设所有联通块都存在环,那么边数必然 \(\ge n\))我们假设构成这个连通块的点集为 \(S\),这就是我们要找的集合 \(S\)。因此如果存在合法的方案,就必定存在符合要求的集合 \(S\)。
那么怎样找出这样的集合 \(S\) 呢?考虑对 \(\sum\limits_{x\in S}d_x=k(|S|-1)\) 进行变形,两边同时减去 \(k|S|\) 可得 \(-k|S|+\sum\limits_{x\in S}d_x=-k\),再将 \(-k\) 分配到求和号中可得 \(\sum\limits_{x\in S}d_x-k=-k\),这个长得一脸 01 背包的样子。我们考虑 \(dp_{i,j}\) 为当前考虑到前 \(i\) 个数,是否存在一个集合 \(S\) 使得 \(\sum_{x\in S}d_x-k=j\),按照就的 01 背包的套路即可,这样复杂度是 \(n^2k\) 的,可以拿到 \(85\) 分。不过发现 \(dp\) 数组每一个值的取值都只有 01 两种可能,故考虑 bitset 优化 \(dp\),具体来说我们对开一个 bitset<MAXN*MAXK*2+5> dp[MAXN+5],其中第 \(i\) 个 bitset 的第 \(j\) 位为 \(1\) 表示 \(dp_{i,j}=1\),否则表示 \(dp_{i,j}=0\)。对于形如 \(dp_{i,j}|=dp_{i-1,j-x}\) 的转移方程,我们就令 \(dp_i|=dp_{i-1}<<x\),显然二者是等价的,复杂度也就降到了 \(\dfrac{n^2k}{\omega}\)。
求完 bitset 之后检验 \(dp_{n,-k}\) 是否等于 \(1\),如果 \(dp_{n,-k}=0\) 则无解,否则按照输出路径的套路找出符合要求的集合 \(S\),然后用 \(m=n-1\) 的算法输出方案即可。
u1s1 bitset yyds!
const int MAXN=500;
const int MAXK=5e3;
int n,m,k,a[MAXN+5];
void work(vector<pii> vx,int n,int m){
set<pii> st;
for(int i=0;i<vx.size();i++) st.insert(vx[i]);
for(int i=1;i<=m;i++){
if(m>=n){
pii pp=*st.rbegin();st.erase(st.find(pp));
printf("%d %d\n",pp.se,k);--m;st.insert(mp(pp.fi-k,pp.se));
} else {
pii p1=*st.begin();st.erase(st.find(p1));
pii pn=*st.rbegin();st.erase(st.find(pn));
printf("%d %d %d %d\n",p1.se,p1.fi,pn.se,k-p1.fi);
st.insert(mp(pn.fi-(k-p1.fi),pn.se));
}
}
}
bitset<MAXN*MAXK*2+5> dp[MAXN+5];
void clear(){
for(int i=0;i<=n;i++) dp[i].reset();
}
vector<pii> v1,v2;
void findpath(int x,int v){
if(!x) return;
if(dp[x-1][v]){
v1.pb(mp(a[x],x));
findpath(x-1,v);
} else {
v2.pb(mp(a[x],x));
findpath(x-1,v-(a[x]-k));
}
}
void solve(){//remember to make it first
scanf("%d%d%d",&n,&m,&k);clear();
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
if(m>=n-1){
vector<pii> v;
for(int i=1;i<=n;i++) v.pb(mp(a[i],i));
work(v,n,m);return;
} int delta=n*k;dp[0][delta]=1;
for(int i=1;i<=n;i++){
int t=a[i]-k;
if(t>=0) dp[i]=dp[i-1]|(dp[i-1]<<t);
else dp[i]=dp[i-1]|(dp[i-1]>>(-t));
}
if(!dp[n][delta-k]){puts("-1");return;}
v1.clear();v2.clear();findpath(n,delta-k);
// for(int i=0;i<v1.size();i++) printf("%d ",v1[i].se);printf("\n");
// for(int i=0;i<v2.size();i++) printf("%d ",v2[i].se);printf("\n");
work(v1,v1.size(),v1.size()-1);
work(v2,v2.size(),v2.size()-1);
}
int main(){
int qu;scanf("%d",&qu);
for(int i=1;i<=qu;i++) solve();
return 0;
}
洛谷 P6775 - [NOI2020] 制作菜品(找性质+bitset 优化 dp)的更多相关文章
- 洛谷P2216: [HAOI2007]理想的正方形 单调队列优化DP
洛谷P2216 )逼着自己写DP 题意: 给定一个带有数字的矩阵,找出一个大小为n*n的矩阵,这个矩阵中最大值减最小值最小. 思路: 先处理出每一行每个格子到前面n个格子中的最大值和最小值.然后对每一 ...
- BZOJ1563/洛谷P1912 诗人小G 【四边形不等式优化dp】
题目链接 洛谷P1912[原题,需输出方案] BZOJ1563[无SPJ,只需输出结果] 题解 四边形不等式 什么是四边形不等式? 一个定义域在整数上的函数\(val(i,j)\),满足对\(\for ...
- 【洛谷3648】[APIO2014] 序列分割(斜率优化DP)
点此看题面 大致题意: 你可以对一个序列进行\(k\)次分割,每次得分为两个块元素和的乘积,求总得分的最大值. 区间\(DPor\)斜率优化\(DP\) 这题目第一眼看上去感觉很明显是区间\(DP\) ...
- 洛谷P2569 (BZOJ1855)[SCOI2010]股票交易 【单调队列优化DP】
Description 最近lxhgww又迷上了投资股票,通过一段时间的观察和学习,他总结出了股票行情的一些规律. 通过一段时间的观察,lxhgww预测到了未来T天内某只股票的走势,第i天的股票买入价 ...
- 洛谷 P2254 [NOI2005]瑰丽华尔兹(单调栈优化DP)
题目描述 不妨认为舞厅是一个N行M列的矩阵,矩阵中的某些方格上堆放了一些家具,其他的则是空地.钢琴可以在空地上滑动,但不能撞上家具或滑出舞厅,否则会损坏钢琴和家具,引来难缠的船长.每个时刻,钢琴都会随 ...
- 洛谷2900 [USACO08MAR]土地征用Land Acquisition (斜率优化+dp)
自闭的一批....为什么斜率优化能这么自闭. 首先看到这个题的第一想法一定是按照一个维度进行排序. 那我们不妨直接按照\(h_i\)排序. 我们令\(dp[i]\)表示到了第\(i\)个矩形的答案是多 ...
- 洛谷3195 [HNOI2008]玩具装箱TOY(斜率优化+dp)
qwq斜率优化好题 第一步还是考虑最朴素的\(dp\) \[dp=dp[j]+(i-j-1+sum[i]-sum[j])^2 \] 设\(f[i]=sum[i]+i\) 那么考虑将上述柿子变成$$dp ...
- 洛谷 P6776 - [NOI2020] 超现实树(找性质,神仙题)
洛谷题面传送门 nb tea 一道! 首先考虑怎样入手分析这个看似非常不可做的问题.首先题目涉及高度无穷的树,根本枚举不了.不过我们冷静一下就会发现,如果我们记 \(mx=\max\limits_{i ...
- 【洛谷 5002】专心OI - 找祖先 (树上计数)
专心OI - 找祖先 题目背景 \(Imakf\)是一个小蒟蒻,他最近刚学了\(LCA\),他在手机\(APP\)里看到一个游戏也叫做\(LCA\)就下载了下来. 题目描述 这个游戏会给出你一棵树,这 ...
随机推荐
- 深入理解java中main方法
理解main方法语法 深入理解main方法: 解释main方法的形式:public static void main(String[] args){} main方法调用者:虚拟机 java虚拟机需要调 ...
- java---String 和 StringBuffer
Java-String和StringBuffer类 Java String 类 字符串在Java中属于对象,Java提供String类来创建和操作字符串. 创建字符串 创建字符串常用的方法如下: ...
- 什么,你还使用 webpack?别人都在用 vite 搭建项目了
一.vite 到底是干嘛的? vite 实际上就是一个面向现代浏览器,基于 ES module 实现了一个更轻快的项目构建打包工具. vite 是法语中轻快的意思. vite 的特点: 1.轻快的冷服 ...
- 【UE4 C++ 基础知识】<10>资源的引用
2种引用方式 硬引用(Hard Reference) 即对象 A 引用对象 B,并导致对象 B 在对象 A 加载时加载 硬引用过多会导致运行时很多暂时用不到的资源也被加载到内存中 大量资源会导致进程阻 ...
- kivy画板
from kivy.app import App from kivy.graphics import Line, Color # 引入绘图线条,颜色 from kivy.uix.widget impo ...
- [对对子队]会议记录4.14(Scrum Meeting 5)
今天已完成的工作 刘子航 工作内容:设计第2,3关 相关issue:设计关卡2,3 吴昭邦 工作内容:制作场景,暂时解决了坐标错位问题 相关issue:实现游戏场景中的必要模型 何瑞 ...
- Vue el 使用el-checkbox-group复选框进行单选框操作
el-checkbox-group这个组件与其他复选框不一样,我当初也是半天不知道怎么操作 页面使用v-model绑定 size就是等比例缩小放大,v-ror循环应该看的懂.重要的是@chage到我们 ...
- 第07课 OpenGL 光照和键盘(1)
光照和键盘控制: 在这一课里,我们将添加光照和键盘控制,它让程序看起来更美观. 这一课我会教您如何使用三种不同的纹理滤波方式.教您如何使用键盘来移动场景中的对象,还会教您在OpenGL场景中应用简单的 ...
- Django 前端BootCSS 实现分页
通过使用bootstrap框架,并配合Django自带的Paginator分页组件即可实现简单的分页效果. 1.创建MyWeb项目 python manage.py startapp MyWeb 2. ...
- C++ 默认拷贝构造函数 深度拷贝和浅拷贝
C++类默认拷贝构造函数的弊端 C++类的中有两个特殊的构造函数,(1)无参构造函数,(2)拷贝构造函数.它们的特殊之处在于: (1) 当类中没有定义任何构造函数时,编译器会默认提供一个无参构造函数且 ...