题目
(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>
",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]
",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));
}
}