题目

\(n\)个节点的树,边权为正整数。

从1 号点开始走一个路径并最终回到 1 号点,且这条路径经过了所有的边。

一条路径的代价就是它经过的边的边权之和。

可以加若干条额外边,第 i 条加的额外边的边权为 正整数\(A_ i\) 。

注意,不一定要经过所有的额外边

对于所有的$ K \in [0, m]$,你需要输出允许加 \(\le K\) 条额外边的最小路径代价。

题解

  • 假设我们选了\(x\)条额外边,答案分为两部分:

    1.在前\(K\)条边里选\(x\)条边,使得长度和最小

    2.在原树上选\(x\)条边不相交的路径使得长度和最大

  • 考虑2,每次确定当前树的直径,将直径上的边全部取反,做\(x\)次即可

    具体用LCT维护ddp

    注意到ddp的话区间取反和翻转无法下放,但只有这两种操作

    可以预处理出splay节点取反,翻转的信息,修改的时候直接调用

  • 由于12都是凸的,所以我们直接三分求答案

  • 时间复杂度:\(O(n \ log ^2 n + n \ log \ n * LCT )\)  

Code

没写出来..先放YFZ的std:

#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+10;
typedef long long ll;
typedef pair<ll,int> pii;
const ll inf=1e18;
int n,fa[maxn],K,C,W;
ll ans,sum,val[maxn],anses[maxn];
pii sval[maxn];
vector<pii>G[maxn]; struct pxx{
ll u,v,w;
pxx(){}
pxx(ll u,ll v,ll w):u(u),v(v),w(w){}
pxx operator+(const ll x)const{return pxx(u,v,w+x);}
pxx operator-(const ll x)const{return pxx(u,v,w-x);}
pxx operator+(const pxx& x)const{
pxx ret;
ret.u=u;ret.v=x.u;
if(ret.u<ret.v)swap(ret.u,ret.v);
ret.w=w+x.w;
return ret;
}
int operator<(const pxx& d)const{
if(w!=d.w)return w<d.w;
if(u!=d.u)return u<d.u;
return v<d.v;
}
};
//定义一条链的结构体
struct data{
pxx pre,suf,ans;ll sum;
data(){}
data(pxx pre,pxx suf,pxx ans,ll sum):pre(pre),suf(suf),ans(ans),sum(sum){}
data operator+(const data& d)const{
data ret;
ret.pre=max(pre,d.pre+sum);
ret.suf=max(suf+d.sum,d.suf);
ret.ans=max(ans,d.ans);
ret.ans=max(ret.ans,suf+d.pre);
ret.sum=sum+d.sum;
return ret;
}
};
//定义一个节点的信息,类似于最大子段和
namespace LCT{
int ch[maxn][2],fa[maxn],tg[maxn],ftg[maxn];
ll val[maxn];
multiset<pxx> st[maxn],stans[maxn];
data dp[maxn];
pxx cpre[maxn],cans[maxn];
data a[2][maxn],ra[2][maxn];
void clr(int n){
for(int i=1;i<=n;++i){
ch[i][0]=ch[i][1]=fa[i]=tg[i]=ftg[i]=val[i]=0;
st[i].clear(),stans[i].clear();
}
memset(dp,0,sizeof(data)*n);
memset(cpre,0,sizeof(pxx)*n);
memset(cans,0,sizeof(pxx)*n);
for(int i=0;i<2;++i){
memset(a[i],0,sizeof(data)*n);
memset(ra[i],0,sizeof(data)*n);
}
}
//
void upd(int o){
int ls=ch[o][0],rs=ch[o][1];
if(o>n){
a[0][o]=ra[0][o]=data(dp[o].pre+val[o],dp[o].pre+val[o],dp[o].ans,val[o]);
a[1][o]=ra[1][o]=data(dp[o].pre-val[o],dp[o].pre-val[o],dp[o].ans,-val[o]);
} else {
a[1][o]=ra[1][o]=
a[0][o]=ra[0][o]=data(dp[o].pre,dp[o].pre,max(dp[o].pre+pxx(o,0,0),dp[o].ans),0);
}
if(ls){
for(int i=0;i<2;++i){
a[i][o]=a[i][ls]+a[i][o];
ra[i][o]=ra[i][o]+ra[i][ls];
}
}
if(rs){
for(int i=0;i<2;++i){
a[i][o]=a[i][o]+a[i][rs];
ra[i][o]=ra[i][rs]+ra[i][o];
}
}
}
//
void _pd(int o,int t,int ft){
if(ft){
ftg[o]^=1;
swap(a[0][o],a[1][o]);
swap(ra[0][o],ra[1][o]);
val[o]=-val[o];
}
if(t){
tg[o]^=1;
swap(ch[o][0],ch[o][1]);
swap(a[0][o],ra[0][o]);
swap(a[1][o],ra[1][o]);
}
}
//修改
void pd(int o){
if(ch[o][0])_pd(ch[o][0],tg[o],ftg[o]);
if(ch[o][1])_pd(ch[o][1],tg[o],ftg[o]);
tg[o]=ftg[o]=0;
}//下放
bool isroot(int x){
if(!x||!fa[x])return true;
return ch[fa[x]][1]!=x&&ch[fa[x]][0]!=x;
}
void rotate(int p){
int q=fa[p],y=fa[q],k=(ch[q][1]==p);
if(!isroot(q))ch[y][ch[y][1]==q]=p;
fa[ch[q][k]=ch[p][k^1]]=q;
fa[ch[p][k^1]=q]=p,fa[p]=y;
upd(q);
}
void splay(int x){
int y;
while(!isroot(x)){
pd(fa[y=fa[x]]),pd(y),pd(x);
if(!isroot(y)){
if((ch[fa[y]][1]==y)^(ch[y][1]==x))rotate(x);
else rotate(y);
}
rotate(x);
}
pd(x),upd(x);
}
//
void uupd(int x){
if(x<=n){
dp[x].pre=*st[x].rbegin();
dp[x].ans=*stans[x].rbegin();
if(st[x].size()>=2){
auto it=st[x].end();
dp[x].ans=max(dp[x].ans,*prev(it)+*prev(prev(it)));
}
} else {
dp[x].pre=st[x].size()?*st[x].rbegin():pxx(0,0,-inf);
dp[x].ans=stans[x].size()?*stans[x].rbegin():pxx(0,0,-inf);
if(st[x].size()>=2){
auto it=st[x].end();
dp[x].ans=max(dp[x].ans,*prev(it)+*prev(prev(it))+val[x]);
}
}
}
//修改一个点的轻链信息
void cadd(int x,int y){
// printf("cadd:(%d)<%lld,%lld,%lld>\n",y,a[0][y].ans.u,a[0][y].ans.v,a[0][y].ans.w);
st[x].insert(a[0][y].pre);
stans[x].insert(a[0][y].ans);
uupd(x);
}
//加入一个轻链信息
void del(int x,int y){
// printf("del:(%d)[%lld,%lld,%lld]\n",y,a[0][y].ans.u,a[0][y].ans.v,a[0][y].ans.w);
st[x].erase(a[0][y].pre);
stans[x].erase(a[0][y].ans);
uupd(x);
}
//删除一个轻链信息
void access(int x){
for(int y=0;x;y=x,x=fa[x]){
splay(x);
// printf("{%d,%d}",x,y);
if(ch[x][1])cadd(x,ch[x][1]);
if(y)del(x,y);
ch[x][1]=y;
upd(x);
}
} void rever(int x){
access(x),splay(x);
_pd(x,1,0),pd(x);
} void link(int u,int v){
access(u),splay(u);
rever(v);
fa[v]=u;
cadd(u,v);
upd(u);
}
void mdy(int u,int v){
rever(u),access(v),splay(v);
_pd(v,0,1),pd(v);
}
void dfs(int u){
pd(u);
if(ch[u][0])dfs(ch[u][0]);
printf("[%d[%d,%d](%lld,%lld,%lld,%d)]",u,ch[u][0],ch[u][1],dp[u].pre.w,a[0][u].ans.w,a[1][u].ans.w,a[1][u].sum);
if(ch[u][1])dfs(ch[u][1]);
}
};
int ptr=0;
void dfs(int u,int f){
for(auto v:G[u])if(v.first!=f){
dfs(v.first,u);
LCT::fa[v.first]=v.second;
LCT::cadd(v.second,v.first);
LCT::fa[v.second]=u;
LCT::upd(v.second);
LCT::cadd(u,v.second);
LCT::upd(u);
}
LCT::upd(u);
}
//?
ll trsum[maxn],trcnt[maxn];
ll calzzz(ll x){
ll nw=0,ans=0,nwcnt=0;
for(int i=21;i>=0;--i){
nw+=(1<<i);
if(nw>K||trcnt[nw]+nwcnt>x)nw-=(1<<i);
else nwcnt+=trcnt[nw],ans+=trsum[nw];
}
return ans+anses[x];
}
//BIT维护1
ll solans(ll K){
int l=0,r=K-1;
ll ans=calzzz(K);
while(l<=r){
int mid=l+r>>1;
if(calzzz(mid)>calzzz(mid+1))l=mid+1,ans=min(ans,calzzz(mid+1));
else r=mid-1,ans=min(ans,calzzz(mid));
}
return ans;
}
//二分斜率求答案
int main(){
freopen("love.in","r",stdin);
freopen("love.out","w",stdout);
scanf("%d%d",&n,&K);
sum=0;
ptr=n;
LCT::clr(2*n);
for(int i=1;i<=n;++i)G[i].clear();
for(int i=1;i<=n;++i){
LCT::st[i].insert(pxx(i,0,0));
LCT::stans[i].insert(pxx(i,i,0));
LCT::uupd(i),LCT::upd(i);
}
for(int i=2,u,v,w;i<=n;++i){
scanf("%d%d%d",&u,&v,&w);
++ptr;
LCT::val[ptr]=w;
LCT::uupd(ptr),LCT::upd(ptr); G[u].push_back(pii(v,ptr));
G[v].push_back(pii(u,ptr));
sum+=2*w;
}
dfs(1,0);
for(int i=1;i<=K;++i)scanf("%lld",&val[i]),sval[i]=pii(val[i],i);
sort(sval+1,sval+K+1);
int flg=0;
anses[0]=sum;
printf("%lld ",solans(0));
for(int i=1;i<=K;++i){
int R=1;
LCT::rever(R);
pxx path=LCT::a[0][R].ans;
sum-=path.w;
anses[i]=sum;
if(path.w==0&&!flg){
flg=1;
fprintf(stderr,"[%d]",i);
}
for(int x=lower_bound(sval+1,sval+K+1,pii(val[i],i))-sval;x<=K;x+=x&-x)
trsum[x]+=val[i],trcnt[x]++;
int u=path.u,v=path.v;
LCT::mdy(u,v);
printf("%lld ",solans(i));
}
}

【JZOJ6232】【20190625】喜欢最最痛的更多相关文章

  1. 5.29 SD省队培训D1

    5.29 SD省队培训D1 自闭的一天 T1 梦批糼 先咕一咕(两天之内一定补上) T2 等你哈苏德 继续咕(一星期之内补上) T3喜欢最最痛 四十分做法: 首先,我们发现同一个点加两条额外边是一件非 ...

  2. 更喜欢从一而终?bing测试在新窗口打开链接遭美国网友痛批

                  原链接地址:http://www.cnbeta.com/articles/186529.htm 我们都知道在中国网站点击一个链接之后,默认在新窗口或新标签打开,大家也很熟悉 ...

  3. [置顶] IT老男人读《因为痛,所以叫青春》

    最近偶然,从别人的书桌上看到这本书,其中有个关于时间的解释,很是让为成功焦虑的老男人受用.   因此,我喜欢将人生的80年跟一天中的24小时进行对照. 人生时钟的计算方法十分简单.24小时相当于144 ...

  4. Eclipse/MyEclipse 最最常用的快捷键

    F 键类 F2 显示详细信息 F3 跳到声明或定义的地方 Ctrl + 键类 Ctrl+1 快速修复 ( 最经典的快捷键 , 就不用多说了 ) Ctrl+D 删除当前行 Ctrl+E 快速显示当前 E ...

  5. Swagger解决你手写API接口文档的痛

    首先,老规矩,我们在接触新事物的时候, 要对之前学习和了解过的东西做一个总结. 01 痛     苦 不做.不行 之前,前后端分离的系统由前端和后端不同的编写,我们苦逼的后端工程师会把自己已经写完的A ...

  6. 早日选择一门自己喜欢的,然后瞄准目标,不达目的誓不罢休。像文章的作者一样成为一名成功的IT人士。

    hawk的奋斗历程. 来自:LinuxForum  :http://www3.linuxforum.net/ 原址:http://www.linuxforum.net/forum/gshowflat. ...

  7. Linq表达式、Lambda表达式你更喜欢哪个?

    什么是Linq表达式?什么是Lambda表达式? 如图: 由此可见Linq表达式和Lambda表达式并没有什么可比性. 那与Lambda表达式相关的整条语句称作什么呢?在微软并没有给出官方的命名,在& ...

  8. PHPStorm配置自己喜欢的主题

    PHPstorm默认的主题和可选的主题有时候不能满足有些人的需求,怎么配置自己喜欢的主题呢? 1.首先先去下载自己喜欢的主题:http://www.phpstorm-themes.com/ 但是在下载 ...

  9. 移动信息化不能延续PC时代的痛

    当下,随着移动时代的到来,手机功能逐步完善,各个行业针对这一现象纷纷制定了相应的营销计划,于是霎时间兴起了一股网上订票/网上订饭/网上预约的热潮. 而对于IT行业,成为企业信息化最火的代名词莫过于移动 ...

随机推荐

  1. laravel hash密码生成和密码验证

    在laravel中 登录表单中的密码是用hash来生成的. 在生成密码需要用到 laravel框架中的方法(都是laravel封装好了的) bcrypt($password)方法,直接将获取到的pas ...

  2. 局域网访问PHP项目网站 用IP地址进入

    先在apache中的 httpd.conf中将 Allow from 127.0.0.1 修改为Allow from all 如果你的是Allow from all的话就不需要改 然后再将 Docum ...

  3. java加密解密工具类

    package com.founder.mrp.util; import java.nio.charset.StandardCharsets; import java.security.Key; im ...

  4. Appium中app的元素定位

    app定位方式,本文只讲Android手机的定位方式. 前提条件是adb连接到模拟器或者是手机(具体连接方式这里不再讲解),证明已连接到设备 adb devices app元素定位工具一:UI Aut ...

  5. 《CI/CD 流程以及原理说明》

    自动化部署 CI/CD 是一种通过在应用开发阶段引入自动化来频繁向客户交付应用的方法.CI/CD 的核心概念是持续集成.持续交付和持续部署.作为一个面向开发和运营团队的解决方案,CI/CD 主要针对在 ...

  6. English--定语从句

    English|定语从句 从介绍从句开始,英语的句子已经开始逐渐复杂了!做好心理准备,三大从句介绍完毕,会介绍分词短语.废话不多说,直接开始干! 前言 目前所有的文章思想格式都是:知识+情感. 知识: ...

  7. 【转载】C#中ArrayList集合类和List集合类的比较

    List集合类和ArrayList集合类都是C#语言中用于存储集合数据的集合类,两者都可灵活的插入.删除以及访问元素等等.但List集合和ArrayList集合的差别还是挺大的,首先List集合类是泛 ...

  8. vue + yarn 项目开发 (一)

    1.打开src文件夹中的main.js文件,添加引用element ui框架 import ElementUI from 'element-ui' import 'element-ui/lib/the ...

  9. MongoDB Spark Connector 实战指南

    Why Spark with MongoDB? 高性能,官方号称 100x faster,因为可以全内存运行,性能提升肯定是很明显的 简单易用,支持 Java.Python.Scala.SQL 等多种 ...

  10. Mybatis如何插入空字段

    出现异常:SQLException : 无效的类型: 1111 使用Mybatis插入一条记录,某字段设为null,出错 尝试将空的mgr属性插入到数据库,解决办法: 添加setting属性jdbcT ...