zoukankan      html  css  js  c++  java
  • CF1192B/CEOI2019 动态直径 Dynamic Diameter【欧拉序-线段树】

    题目链接

    题目解析

    唔,首先有一个比较显然的大概是(nq)级别复杂度的做法,就是暴力修改,然后(dfs)两次算出直径。

    (不过这没有什么用就是了

    因为有修改,我们尝试把树下下来,放到序列上,用线段树维护。

    树链剖分?

    不,太麻烦喏,我们知道欧拉序这个东西是可以办到的,并且我们之前就用它的性质求过(lca):两个点对应的欧拉序区间中,深度最小的点就是这两个点的(lca)

    具体而言,设(u)在欧拉序中第一次出现的位置为(l)(v)在欧拉序中第一次出现的位置为(r),欧拉序数组为(E[]),那么(lca=E[x],l<=x<=r,dep[E[x]]<=dep[E[l...r]])

    如果不知道这个结论可以看看 这个

    那么(dist(u,v)=dis[u]+dis[v]-2 imes dis[lca])(dis[i])指从(1)(i)的距离。

    根据定义,树的直径是所有点对(dist)的最大值

    如果用线段树维护(相当于是线段树维护与欧拉序数组对应的点的(dis),长度为(2n-1),完成把树变成序列的任务),那对于区间([l,r])的答案长这个样子:

    (max{dis[i]+dis[j]-2 imes dis[lca(i,j)]},l<=i<=j<=r)

    我们注意到(lca)对于(i,j)来说是一个(rmq)问题,那......线段树套线段树?

    没有必要。

    具体到这道题而言,由于边权都是正数,所以(lca)也是(dis)最小的那个点,所以可以把答案化为这个样子:

    (max{dis[i]+dis[j]-2 imes dis[k] },l<=i<=k<=j<=r)

    因为(dis)前面是负号,所以让这个值取(max)的那个位置一定是(dis[k])最小的那个点,所以可以不用再维护([i,j])之间的最小值,而是在维护上述答案的时候自然就处理了(我感觉这里网上很多题解都没有说清楚,我看了好久)


    接下来考虑怎么维护上述答案,主要是要便于区间合并。

    我们可以把答案看成三部分:(dis[i],dis[j],-2 imes dis[k])

    对于每个区间,维护:

    (mx=max(dis[i]),l<=i<=r)

    (mn=min(dis[i]),l<=i<=r),(主要是为了(-2 imes dis[k]),维护(max(-dis[i]))也可

    (res=max{dis[i]+dis[j]-2 imes dis[k] },l<=i<=k<=j<=r)

    为了方便左右儿子合并,也就是(i,j)可以来源于左右不同儿子,合在一起是一个更优的答案,我们还需要维护以下信息:

    (rmx=max(dis[i]-2 imes dis[j]),i<=j),这个可以看成我们还需要在右边找一个右端点来组成答案。

    同理,(lmx=max(dis[i]-2 imes dis[j]),i<=j)


    然后维护线段树:

    (t.mx=max(lc.mx,rc.mx))

    (t.mn=min(lc.mn,rc.mn))

    (t.res=max(max(lc.res,rc.res),max(lc.rmx+rc.mx,rc.lmx+lc.mx)))

    (t.rmx=max(max(lc.rmx,rc.rmx),lc.mx-2rc.mn))

    (t.lmx=max(max(lc.lmx,rc.lmx),rc.mx-2lc.mn))

    最后查询的时候是查询整棵树的直径,所以不用写(query)


    修改的时候,答案只会对这条边深度较大的点的子树的答案产生影响,所以对子树进行更新即可(子树在欧拉序中是连续一段区间


    ►Code View

    #include<cstdio>
    #include<algorithm>
    #include<queue>
    #include<cstring>
    using namespace std;
    #define N 200005
    #define M 200005
    #define MOD 998244353
    #define INF 0x3f3f3f3f
    #define LL long long
    LL rd()
    {
    	LL x=0,f=1;char c=getchar();
    	while(c<'0'||c>'9'){if(c=='-') f=-1; c=getchar();}
    	while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+(c^48); c=getchar();}
    	return f*x;
    }
    struct edge{
    	int nxt,v;LL w;
    }e[M<<1];
    int hd[N],cnt=1;
    void add(int u,int v,LL w)
    {
    	e[++cnt].nxt=hd[u];
    	e[cnt].v=v;
    	e[cnt].w=w;
    	hd[u]=cnt;
    }
    
    struct node{
    	LL res/*直径*/,mx/*dis最大*/,mn/*dis最小*/;
    	LL rmx/*dis_i-2dis_j(i<=j)的最大值 差一个右端点*/,lmx/*同理 dis_i-2dis_j(i>=j)的最大值*/;
    }tree[N<<2];
    LL tag[N<<2];
    int n,Q;
    LL W;
    LL dis[N];
    int E[N<<1],tim;//欧拉序 
    int d1[N],d2[N];//出去进来的时间 即点i在欧拉序中的最左/最右位置 
    void dfs(int u,int fa)
    {
    	E[++tim]=u;
    	d1[u]=tim;
    	for(int i=hd[u];i;i=e[i].nxt)
    	{
    		int v=e[i].v;LL w=e[i].w;
    		if(v==fa) continue;
    		dis[v]=dis[u]+w;
    		dfs(v,u);
    		E[++tim]=u;
    	}
    	d2[u]=tim;
    }
    node Merge(node x,node y)
    {
    	node t;
    	t.mx=max(x.mx,y.mx);
    	t.mn=min(x.mn,y.mn);
    	t.rmx=max(max(x.rmx,y.rmx),x.mx-2*y.mn);
    	t.lmx=max(max(x.lmx,y.lmx),y.mx-2*x.mn);
    	t.res=max(max(x.res,y.res),max(x.mx+y.lmx,y.mx+x.rmx));
    	return t;
    }
    void PushUp(int i)
    {
    	tree[i]=Merge(tree[i<<1],tree[i<<1|1]);
    }
    void Build(int i,int l,int r)
    {
    	if(l==r)
    	{
    		int u=E[l];
    		tree[i].mx=tree[i].mn=dis[u];
    		tree[i].rmx=tree[i].lmx=-dis[u];//dis_u-2*dis_u=-dis_u
    		tree[i].res=0;//一个点不能构成直径
    		tag[i]=0;
    		return ;
    	}
    	int mid=(l+r)>>1;
    	Build(i<<1,l,mid);
    	Build(i<<1|1,mid+1,r);
    	PushUp(i);
    	return ;
    }
    void Modify(int i,LL val)
    {
    	tree[i].mx+=val,tree[i].mn+=val;
    	tree[i].lmx-=val,tree[i].rmx-=val;//delta=(dis_i-val)-2*(dis_j-val)-(dis_i-2*dis_j)
    	tag[i]+=val;
    }
    void PushDown(int i)
    {
    	if(tag[i])
    	{
    		Modify(i<<1,tag[i]);
    		Modify(i<<1|1,tag[i]);
    		tag[i]=0;
    	}
    }
    void Update(int i,int l,int r,int ql,int qr,LL val)
    {
    	if(ql<=l&&r<=qr)
    	{
    		Modify(i,val);
    		return ;
    	}
    	PushDown(i);
    	int mid=(l+r)>>1;
    	if(ql<=mid) Update(i<<1,l,mid,ql,qr,val);
    	if(qr>mid) Update(i<<1|1,mid+1,r,ql,qr,val);
    	PushUp(i);
    }
    int main()
    {
    	n=rd(),Q=rd(),W=rd();
    	for(int i=1;i<=n-1;i++)
    	{
    		int u=rd(),v=rd();LL w=rd();
    		add(u,v,w);
    		add(v,u,w);
    	}
    	dfs(1,0);
    	Build(1,1,tim);
    	LL ans=0;
    	while(Q--)
    	{
    		int id=rd(),u;LL d=rd();
    		id=(id+ans)%(n-1)+1;
    		d=(d+ans)%W;
    		if(dis[e[id<<1].v]<dis[e[id<<1|1].v]) id=(id<<1|1),u=e[id].v;
    		else id=(id<<1),u=e[id].v;
    		Update(1,1,tim,d1[u],d2[u],d-e[id].w);
    		e[id].w=e[id^1].w=d;
    		ans=tree[1].res;
    		printf("%lld
    ",ans);
    	}
    	return 0;
    }
    /*
    修改是给的边的编号 这个时候还是写前向星好一点吧
    (怎么感觉vector一无是处了qwq 
    */
    
  • 相关阅读:
    matlab中绘制折线图、绘制条形图(柱形图)的简单方法
    2.序列类型方法
    年度KPI绩效面谈记录
    软件开发工作杂谈——更杂版
    软件研发工作杂谈
    FFI Navigtor在Windows环境下的"No Such File or Directory"Bug解决过程
    Linux环境部署帆软报表(FineReport)说明
    IIS负载均衡Application Request Route 安装配置说明(ARR)
    python使用了resetKeyboard输入法打不开怎么办
    python 执行时报错AttributeError: 'dict' object has no attribute 'has_key'
  • 原文地址:https://www.cnblogs.com/lyttt/p/14075116.html
Copyright © 2011-2022 走看看