暑假集训 加赛1
1.高一上七月上旬日记2.牛客周赛 Round 493.Denso Create Programming Contest 2024(AtCoder Beginner Contest 361)4.NOIP2024模拟15.NOIP2024模拟26.高一上七月中旬日记7.NOIP2024模拟38.CSP提高组模拟19.暑假集训CSP提高模拟110.暑假集训CSP提高模拟211.暑假集训CSP提高模拟312.暑假集训CSP提高模拟413.高一上七月下旬日记
14.暑假集训 加赛1
15.暑假集训CSP提高模拟516.暑假集训CSP提高模拟617.加赛218.暑假集训CSP提高模拟719.暑假集训CSP提高模拟820.暑假集训CSP提高模拟921.暑假集训CSP提高模拟1022.暑假集训CSP提高模拟1123.暑假集训CSP提高模拟12暑假集训 加赛1
组题人: @Chen_jr
P145 修仙(restart)
-
题目中的 可以 指 恰好 。
-
考虑计算双休带来的差值。
-
令
,则需要计算从 中选出 个不相邻的 的最大价值,同 luogu P3620 [APIO/CTSC2007] 数据备份 | CF958E2 Guard Duty (medium) | SP1553 BACKUP - Backup Files | luogu P1484 种树 | luogu P1792 [国家集训队] 种树。 -
然后就是反悔贪心板子了。
- 基本思想是无论当前的选项是否最优都接受,然后进行比较,如果选择之后不是最优了,则反悔,舍弃掉这个选项,常写作两种决策之间作差的形式;否则,正式接受。如此往复。
- 容易有在求解最大值/最小值的最优解中,最大值/最小值左右两端的数要么都选要么都不选。
的状态能由 的状态继承而来,启发我们可以依次处理子问题。- 算法流程:先选出
中的最大值 加入答案集合,然后删除 ,用 替代原来的 。- 若选了
等价于将原来答案集合里的 替代成 。 - 否则选择
。
- 若选了
- 双向链表维护动态插入和删除;优先队列维护每次选出最大值,需要懒惰删除法。
- 边界
和 一般需要特殊处理。- 实际上是对
的处理。
- 实际上是对
点击查看代码
priority_queue<pair<ll,ll> >q; ll a[200010],b[200010],cha[200010],l[200010],r[200010],vis[200010]; int main() { ll n,x,k,ans=0,pos,i; cin>>n>>x; k=n/2; for(i=1;i<=n;i++) { cin>>a[i]; ans+=a[i]; } for(i=1;i<=n-1;i++) { b[i]=a[i]+a[i+1]-max(a[i]+a[i+1]-x,0ll); l[i]=i-1; r[i]=i+1; q.push(make_pair(b[i],i)); } b[0]=b[n]=-0x3f3f3f3f3f3f3f3f;//保证不会被选到 for(i=1;i<=k;i++) { while(q.empty()==0&&vis[q.top().second]==1) { q.pop(); } if(q.empty()==0) { ans-=q.top().first; pos=q.top().second; q.pop(); vis[l[pos]]=vis[r[pos]]=1; b[pos]=b[l[pos]]+b[r[pos]]-b[pos]; q.push(make_pair(b[pos],pos)); l[pos]=l[l[pos]]; r[pos]=r[r[pos]]; l[r[pos]]=r[l[pos]]=pos; } cout<<ans<<endl; } return 0; }
T3682. 七负我
- 不难发现,完全子图对答案的贡献最大。
- 设最后得到的最大子图/团大小为
,则 即为所求。 - 现在问题来到了怎么求最大子图/团,常采用 Bron–Kerbosch 算法或折半搜索。
- Bron–Kerbosch 算法
-
维护
三个集合,分别表示当前正在找的极大团里的点/有可能加入当前正在找的极大团里的点/已经找到的极大团内的点。 用于判断找到的团先前有没有找过,在统计最大团方案数时需要用到。
-
算法流程如下
初始化 为空集, 为包含所有节点的集合。 取 中一点 ,设 表示所有与 有边相邻的点的集合,递归集合 。递归过程中若出现 均为空的情况,则 内部的点就构成了一个极大子团,统计答案。- 要保证加入
后仍是团,必须保证加入后 中所有点都与 相连,故取并集。- 这实际上是设定关键点优化的一部分。
- 要保证加入
将 从 中删除,并加入到 中。 重复 操作直至 为空。
-
优化
- 及时剪枝
- 在开始时把所有点排序,枚举时按照下标顺序,防止重复
- 设定关键点
- 若将
加入 后,再取与 有边相连的点 加入 后仍是一个极大团,那么加入 的顺序并不影响答案。 - 所以将
加入 后,再取出与 没有边相连的点进行递归。
- 若将
点击查看代码
int dis[50][50],R[50][50],P[50][50],X[50][50],siz=0; void Bron_Kerbosch(int dep,int r,int p,int x)//dep 表示当前搜到第几层 { if(p==0&&x==0)//若 x 不等于 0 说明这个团之前已经算过了,才能用来统计方案数 { siz=max(siz,r); } else { int u=P[dep][1]; for(int i=1;i<=p;i++) { if(dis[u][P[dep][i]]==0)//因为 dis[P[dep][1]][P[dep][1]]=0 所以一开始会算一次 { for(int j=1;j<=r;j++)//继承 { R[dep+1][j]=R[dep][j]; } R[dep+1][r+1]=P[dep][i];//加入 int np=0,nx=0; for(int j=1;j<=p;j++) { if(dis[P[dep][i]][P[dep][j]]!=0) { np++; P[dep+1][np]=P[dep][j];//取并集 } } for(int j=1;j<=x;j++) { if(dis[P[dep][i]][X[dep][j]]!=0) { nx++; X[dep+1][nx]=X[dep][j];//取并集 } } Bron_Kerbosch(dep+1,r+1,np,nx); x++; X[dep][x]=P[dep][i];//加入 P[dep][i]=0;//删除 } } } } int main() { int n,m,x,u,v,i; cin>>n>>m>>x; for(i=1;i<=m;i++) { cin>>u>>v; dis[u][v]=dis[v][u]=1; } for(i=1;i<=n;i++) { P[1][i]=i; } Bron_Kerbosch(1,0,n,0); printf("%.6lf\n",(1.0*x/siz)*(1.0*x/siz)*siz*(siz-1)/2); return 0; }
-
- 折半搜索
- 以下部分贺的官方题解,改了下
。我们将所有点分为两个集合
,对于 中的所有点,维护 表示集合 中所有点组成子图中最大团大小。之后枚举
中的最大团 ,找到最大团中所有节点向 集合连边的交集,显然 可以更新答案。
- 以下部分贺的官方题解,改了下
- Bron–Kerbosch 算法
总结
- 科普场。
- 反悔贪心是去年听
的多校联训的时候写的,只学了写法,没学怎么将题面转化成反悔贪心可解决的问题,导致 没看出来是反悔贪心。
后记
-
成分复杂。
本文来自博客园,作者:hzoi_Shadow,原文链接:https://www.cnblogs.com/The-Shadow-Dragon/p/18315649,未经允许严禁转载。
版权声明:本作品采用 「署名-非商业性使用-相同方式共享 4.0 国际」许可协议(CC BY-NC-SA 4.0) 进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】