CF1192B - Dynamic Diameter (动态维护直径:全dfs序+线段树)

上海网络赛A前置技能题。

大致题意

给定一颗树,树边有边权,每次修改一条边的边权,要求输出当前树的直径的长度,强制在线。

思路

https://www.cnblogs.com/TinyWong/p/11260601.html
上海网络赛A的题解推荐的巨巨博客,写了三种方法搞这个题。

  1. 树链剖分+lca nlog2n splay+lca nlogn
  2. 动态dp nlogn
  3. 全dfs序 nlogn

由于蒟蒻前两个不会…只能学一下第三种。全dfs序是一个类似欧拉序的东西,可以理解为完全描述了dfs搜索的路径,普通的dfs序没有描述回溯的时间戳,而全dfs序的回溯路径也会被记录,所以总共有n个节点加n-1条回溯边,所以总共有2*n-1个时间戳。修改边的操作还是把边权搞成点权,思路同POJ2763。用线段树维护时间戳序列,任意两个时间戳之间代表了一段树上的移动路径。
每一段路径维护这样几个信息:

  1. val 表示这段路径上距离根最远的距离 max_dis[x]
  2. M 表示这段路径上距离根最近的距离 min_dis[x] ,作为lca,方便计算记录其为 -2dis[x]。
  3. LM 表示选择左端点以及lca的最长距离。
  4. MR 表示选择右端点以及lca的最长距离。
  5. LMR 表示选择左lca右 也就是完整的一条链的最长长度。
代码
#include<bits/stdc++.h>
using namespace std;
#define maxn 200005
#define maxm 1000006
#define ll long long int
#define INF 0x3f3f3f3f
#define inc(i,l,r) for(int i=l;i<=r;i++)
#define dec(i,r,l) for(int i=r;i>=l;i--)
#define mem(a) memset(a,0,sizeof(a))
#define sqr(x) (x*x)
#define inf (ll)2e18+1
#define PI acos(-1)
#define ls x<<1
#define rs x<<1|1
ll read(){
    ll x=0,f=1ll;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
     while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
     return f*x;
}
int n,m;ll W;
struct edge{int u,v;ll w;int nxt;}ed[maxn<<1];
int head[maxn],tot;
void add(int x,int y,ll z){ed[++tot]={x,y,z,head[x]};head[x]=tot; }
int id[maxn<<1],in[maxn],out[maxn],cnt,dep[maxn];
ll dis[maxn];
void dfs(int x,int y){
    dep[x]=dep[y]+1;
    id[in[x]=++cnt]=x;
    for(int i=head[x];i;i=ed[i].nxt){
        int v=ed[i].v;
        if(v==y)continue;
        dis[v]=dis[x]+ed[i].w;
        dfs(v,x);
        id[++cnt]=x;
    }
    out[x]=cnt;
}
struct node{ll val,M,LM,MR,LMR,laz;}t[maxn<<2];
void pushdown(int x){
    t[ls].laz+=t[x].laz;t[rs].laz+=t[x].laz;
    t[ls].val+=t[x].laz;t[rs].val+=t[x].laz;
    t[ls].M-=2*t[x].laz;t[rs].M-=2*t[x].laz;
    t[ls].LM-=t[x].laz;t[rs].LM-=t[x].laz;
    t[ls].MR-=t[x].laz;t[rs].MR-=t[x].laz;
    t[x].laz=0;
}
void pushup(int x){
    t[x].val=max(t[ls].val,t[rs].val);
    t[x].M=max(t[ls].M,t[rs].M);
    t[x].LM=max(max(t[ls].LM,t[rs].LM),t[ls].val+t[rs].M);
    t[x].MR=max(max(t[ls].MR,t[rs].MR),t[ls].M+t[rs].val);
    t[x].LMR=max(max(t[ls].LMR,t[rs].LMR),max(t[ls].val+t[rs].MR,t[ls].LM+t[rs].val));
}
void build(int x,int l,int r){
    if(l==r){
        ll d=dis[id[l]];
        t[x].val=d;
        t[x].M=-2*d;
        t[x].LM=t[x].MR=-d;
        t[x].LMR=t[x].laz=0;
        return ;
    }
    int mid=(l+r)>>1;
    build(ls,l,mid);build(rs,mid+1,r);
    pushup(x);
}
void update(int x,int l,int r,int lp,int rp,int s){
    if(l<r&&t[x].laz)pushdown(x);
    if(lp<=l&&r<=rp){
        t[x].laz+=s;
        t[x].val+=s;
        t[x].LM-=s;t[x].MR-=s;
        t[x].M-=2*s;
        return ;
    }
    int mid=(l+r)>>1;
    if(lp<=mid)update(ls,l,mid,lp,rp,s);
    if(mid+1<=rp)update(rs,mid+1,r,lp,rp,s);
    pushup(x);
}
int main()
{
    n=read();m=read();W=read();
    int x,y;ll z;
    inc(i,1,n-1){
        x=read();y=read();z=read();
        add(x,y,z);add(y,x,z);
    }
    dfs(1,0);
    build(1,1,cnt);
    ll ans=0;
    while(m--){
        x=((read()+ans)%(n-1))+1;z=(read()+ans)%W;
        int u=ed[2*x-1].u;
        int v=ed[2*x-1].v;
        int xx=(dep[u]<dep[v])?v:u;
        update(1,1,cnt,in[xx],out[xx],z-ed[2*x-1].w);
        ed[2*x-1].w=z;
        printf("%lld\n",ans=t[1].LMR);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值