博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
bzoj 5252: [2018多省省队联测]林克卡特树
阅读量:5055 次
发布时间:2019-06-12

本文共 2502 字,大约阅读时间需要 8 分钟。

Description

小L 最近沉迷于塞尔达传说:荒野之息(The Legend of Zelda: Breath of The Wild)无法自拔,他尤其喜欢游戏中的迷你挑战。

游戏中有一个叫做“LCT” 的挑战,它的规则是这样子的:现在有一个N 个点的 树(Tree),每条边有一个整数边权vi ,若vi >= 0,表示走这条边会获得vi 的收益;若vi < 0 ,则表示走这条边需要支付- vi 的过路费。小L 需要控制主角Link 切掉(Cut)树上的 恰好K 条边,然后再连接 K 条边权为 0 的边,得到一棵新的树。接着,他会选择树上的两个点p; q ,并沿着树上连接这两点的简单路径从p 走到q ,并为经过的每条边支付过路费/ 获取相应收益。
海拉鲁大陆之神TemporaryDO 想考验一下Link。他告诉Link,如果Link 能切掉 合适的边、选择合适的路径从而使 总收益 - 总过路费最大化的话,就把传说中的大师之剑送给他。
小 L 想得到大师之剑,于是他找到了你来帮忙,请你告诉他,Link 能得到的 总收益 - 总过路费最大是多少。

Solution

凸优化

发现斜率是递增的,虽然后面可能会变成负的,二分这个斜率 \(mid\)
然后相当于选择若干条点不相交的路径,每一次选择的代价为 \(mid\),求 \((K,f(K))\) 的值
我们二分到 \((K,f(K))\) 所在直线的斜率就可以推出这个点的值

求函数的最大值和 \(60\)\(DP\) 一样,设 \(f[x][0/1/2]\) 表示这个点的度数为 \(0/1/2\) 时的最大值

转移时顺便维护选择的路径数量,具体转移见代码

#include
using namespace std;template
void gi(T &x){ int f;char c; for(f=1,c=getchar();c<'0'||c>'9';c=getchar())if(c=='-')f=-1; for(x=0;c<='9'&&c>='0';c=getchar())x=x*10+(c&15);x*=f;}typedef long long ll;const int N=3e5+10;const ll inf=1e15;int n,K,head[N],nxt[N*2],to[N*2],num=0,dis[N*2];ll reb,mid;inline void link(int x,int y,int z){ nxt[++num]=head[x];to[num]=y;head[x]=num;dis[num]=z; nxt[++num]=head[y];to[num]=x;head[y]=num;dis[num]=z;}struct data{int x;ll y;}f[N][3];inline bool operator <(const data &p,const data &q){ return p.y!=q.y?p.y
q.x;}inline data operator +(const data &p,const data &q){ return (data){p.x+q.x,p.y+q.y};}inline data operator +(const data &p,const ll q){return (data){p.x,p.y+q};}inline void upd(data &a,data b,int c){b.x+=c;a=max(a,b);}inline void dfs(int x,int last){ f[x][0]=(data){0,0};f[x][1]=(data){1,-mid};f[x][2]=(data){0,-inf}; for(int i=head[x],u;i;i=nxt[i]){ if((u=to[i])==last)continue; dfs(u,x); data w=max(f[u][0],max(f[u][1],f[u][2])); upd(f[x][2],f[x][2]+w,0); upd(f[x][2],f[x][1]+f[u][1]+mid+dis[i],-1); upd(f[x][1],f[x][1]+w,0); upd(f[x][1],f[x][0]+f[u][1]+dis[i],0); upd(f[x][0],f[x][0]+w,0); }}inline int solve(){ dfs(1,1); data ans=(data){0,-inf}; for(int i=1;i<=n;i++)ans=max(ans,max(f[i][0],max(f[i][1],f[i][2]))); if(ans.x<=K)reb=ans.y; return ans.x;}int main(){ freopen("pp.in","r",stdin); freopen("pp.out","w",stdout); int x,y,z; ll l=0,r=0,ret=0; cin>>n>>K;K++; for(int i=1;i
>1; if(solve()<=K)ret=mid,r=mid-1; else l=mid+1; } reb+=ret*K; cout<
<

转载于:https://www.cnblogs.com/Yuzao/p/9085605.html

你可能感兴趣的文章
.双向冒泡:
查看>>
web测试经验整理
查看>>
Android多线程(二)
查看>>
常用的jvm命令
查看>>
(HDU)1092 --A+B for Input-Output Practice (IV)(输入输出练习(IV))
查看>>
OA学习笔记-004-Spring2.5配置
查看>>
PLSQL 循环结构
查看>>
day11 函数对象
查看>>
visualSFM
查看>>
阅读全部教材后的问题
查看>>
html5中的video标签和audio标签
查看>>
js 里面的jion方法
查看>>
逻辑卷在卷组中不显示
查看>>
Thymeleaf模板引擎
查看>>
SpringMVC与请求控制器
查看>>
SQL实现向一张表中插入数据,其编号为已有的最大编号加一
查看>>
mySql中SUBSTRING_INDEX函数用法
查看>>
开发环境、测试环境、预发布环境、生产环境的区别
查看>>
linux后台运行程序--nobup
查看>>
7-大数斐波那契额数列
查看>>