跟贪心杂题爆了
基本都抄的,窝怎么这么渺小啊
AGC007F
这种匹配可行性基本都是从后往前贪心,这样没有后效性。
而我们考虑原序列的每个字符都对应了最后序列的一个区间(如果用上)。
考虑把整个变化过程写成一个矩阵,并且将每个字符染上不同颜色。
像这样:
容易发现对于一条新的路径,我们尽可能与上一条路径贴合最优。因为
所以答案就是整个图最大连续(左下斜线)的右拐点个数。其实也是纵坐标。
考虑一个路径的拐点,与上一条路径贴和的话,就是上一个拐点的左下的点是当前路径的拐点。
所以可以存下每个拐点的路径起始位置
每一次添加一条新路径时,那些 while(h<=t&&q[h]+h-t>i)++h;
所以这条路径的拐点会由剩下的原本拐点向左下走一步更新,而新拐点就从匹配位置起始(原序列的位置,这个位置向下走一步就开始右拐,这就是最上面的那个拐点)。q[++t]=j;
不难发现更新次数与当前拐点是队列里第几个对应(在之后插入了多少个拐点)。if(j!=i)ans=max(ans,h-t+2)
,这是因为自带一个拐点,要特判掉直接往下走的情况。
AGC009 D
题意就是要求构造一颗原树的点分树,要求最大深度最小,求这个最小的最大深度。
不妨模仿求直径,我们设
那么根据点分树的性质:两点
可以得出一个必要条件:
同时我们说明这是充分的。
考虑每次取出最大
如果某个连通块存在两个最大的
所以这个条件充要。
那么问题就变成了我们给所有点分配点权,使得最大的点权最小,且满足任意两个相同点权,路径上必然有点点权大于它们。
不妨考虑状压
设
那么对于
从这里我们可以发现,对于每个值,取能取的最小值没有后效性,且一定最优。
所以我们不妨设
因此我们任意钦定一个根,贪心的由叶子向根构造即可(原本是叶子在点分树上一定也是叶子,因为只有一个方向)。
那么可以求出
则我们所求的 builtin
系列函数做到
根据点分治的知识我们知道答案不超过 unsigned int
的数状压即可。
将最优化问题变成树上赋权问题,这很妙啊。
AGC 018C
显然先拆分贡献,假定所有点都取
考虑两个
P5659
可以注意到,删边是假的,可以看作每条边只能够操作一次。
注意到交换后这个编号将永困子树中
但是可以注意到不妨以节点 1 为根,最差我们可以让第一个数字是1,这就要求了一个数字1到节点1操作的一个篇序关系
能否更进一步呢?
可以看出,我们最终的目的是保证i-1所对应节点的编号最小的情况下让i最小
俄
一个暴力就出来了,暴力枚举i,枚举其对应节点,建立偏序关系,判断是否成环
O(n^3)
这一定是核心做法——枚举对应节点判断是否合法
考虑下部分分
菊花图的情况
考虑一个点的所有出边的操作顺序?
由于菊花,所以我们可以完全可以把下次操作的边调整到当前操作边的旁边,会产生一个
会发现除了花心之外,我们可以适当调整剩下的顺序
可以等价为将花心移动到第一次操作的那个位置,并将最后一次操作的那个点变成花心,剩下的依次旋转
这相当于是一个置换,不妨设权值为i的点编号是p[i],那么相当于对 p 作置换
满足置换环是一个大环即可
目的是让p最终字典序最小,那么p[1]与最小的连边,然后删去p[1],继续决策即可
链的情况偏序关系
我们只需要关心相邻两个边的操作
那么首先我们让
矛盾即非法,这已经足够了
最后可以还原方案
一般的情况
这启发我们考虑一个点相邻边操作的偏序关系,而且如果没有公用顶点的边的偏序关系是不重要的
不妨维护这个偏序关系,肯定用链表维护局部吧
那么指定移动相当于给了新的偏序关系,这要求中间点的这两边必须相邻操作,同时起点边必须第一次操作,同时最终边也有较多限制
默然边指
- 对起边的限制
- 这条边的不能够已经两用了,也不能够向反方向移动,也就是这个方向没有数字占用
- 这条边所在偏序关系链不能够不完整
也就是指如果当前点 已经指定有数字将会换进来,这个必然是最后操作,同时也就提供了这个偏序关系(也就是我们已经最后一次操作(换进来数字),也即将指定第一次操作(当前换出去))如果仍然有边没有参与那么一定非法
- 对中间边的限制
- 这条边这个方向被使用过非法
- 偏序关系成环非法
- 当前点的边如果已经知晓始末并且这条边加入后还有游离边非法
- 对终边的限制
- 这个方向被使用过非法
- 偏序关系链如果已经确定头尾且不完整非法
实现较为复杂,我们将一条边的状态记作
作一次 dfs,将当前所需点设为根 判断合法,然后取最小的出来往上跳更新信息
CF335F
335e已经够恶心了
题意其实是给定序列
- 将
连边,存在完备匹配 最小
假定没有重复元素,可以想到将饼子按照
可以
如果有重复元素怎么办呢?
那就不妨设
那么有:
仍然可以
signed main(){
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
cin>>n;
for(int i=1;i<=n;++i)cin>>a[i];
sort(a+1,a+n+1,[](int a,int b){return a>b;});
int m=0;
for(int i=1;i<=n;++i){
if(a[i]!=a[i-1]){
++m;a[m]=a[i];
}
++c[m];
}
memset(f,0x3f,sizeof f);
f[0][0]=0;
for(int i=0,s=0;i<m;++i){
s+=a[i];
for(int j=0;j<=s;++j)for(int k=0;k<=c[i+1];++k){
if(j>=c[i+1]-k)f[i+1][j-c[i+1]+k*2]=min(f[i+1][j-c[i+1]+k*2],f[i][j]+k*a[i+1]);
}
}int res=0x3f3f3f3f3f3f3f3fll;
for(int j=0;j<=n;++j)res=min(res,f[m][j]);
cout<<res<<"\n";
}
好了现在我们给出了一个
- Slope Trick
- 贪心
好像有凸性,只看
但不是很好弄阿
回到最开始的条件,我们假设不存在完备匹配,应当可以调整
例如如果有
考虑反悔贪心,用一个堆来维护当前所有的赠品,遇到一个饼子的时候,如果还可以白拿就白拿,否则换一个赠品给钱白拿这个或者直接买了这个
后面这两决策谁更优秀?
换赠品给钱什么时候会优于后面的
显然不会,但是能白拿就白拿感觉是错的
当前白拿,后面给钱,代价
当前给钱,后面白拿两个,代价
开个堆,维护当前这个白拿的换成给钱得给多少。
贪心比对决策就好了
详细地说,我们考虑维护一个小根堆,里面维护 当前所有白嫖决策的花费
当考虑新加入
设
-
(这是可能发生的,会有复杂的白嫖方案)将其替换为白嫖
是不劣的,买了它后还能多嫖一个,所以新的白嫖方案是舍弃 ,嫖两个 -
此时考虑
,这是买 白嫖两个 的收益,我们当前面临选择哪个决策更好,我们可以当作白嫖了物品 (如果 的话)和物品 因为它后续影响是一样的(你可以看作两个方案之间转化的代价)
P6331
考虑差分,设
发现好像很复杂,换个角度考虑。
首先我们可以调整操作二操作三的区间范围让其端点被修改。
所以我们可以统称操作二和操作三为第二类操作
发现对于
这启发我们考虑
首先进行 次操作一,接着 变为零,此刻要么使用二操作扩展要么就自己消自己,压力会给到 ,若 足够 进行第二类操作消完那就给到 ,否则将 先调整到 然后操作(这个调整同样可以选第二类操作,所以只会作第二类操作) 直接做第一类操作显然是最优的。
因为每一类操作的代价是一样的,我们默然对于左端点相同的操作先进行一类。
我想我们将尽量将操作联动,考虑动态维护当前有多少一/二类可以扩展到
譬如设当前有
显然全部扩展过来就好了
考虑仅有第一类操作,虽然我们可以直接算出下界,但是还是回忆下它的过程,其实就是一个直接继承的过程,那么在这里我们先强制继承第一类操作是否更优?
显然是的,如果你这里继承了第二类下一次又被强行要求取第二类就违反了这个策略:相邻两个数不会同时进行第二类操作
但我们应当继承多少?全部继承么?
我们会余出
综上所述,我们维护
设当前操作的是
- 分别对
取 - 若
设 ,这是延迟操作的量,然后我们进行 次操作将 降为 ,但为了延迟决策假定现在是 - 若
显然直接全用了最好 - 然后考虑增加操作:
需要更多的
若 还需要 次 (注意这里延伸到了 ) - 如果延迟了决策那就将
重新赋为 。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!