cf 1434D. Roads and Ramen (树上最长偶权链)

题目链接:传送门

题目思路:

显然,最长的偶权链的两个端点中,至少有一个是直径的端点。

简单证明一下,

若直径的权为偶,那么最长链肯定就是直径

若直径的权为奇,那么对于直径上任意一个点可以把直径分割为一段奇权链和一段偶权链

那么可以使用反证法,假设最长的偶权链的两个端点都不是直径的端点。如下图所示:

首先明确一点:一个点在树上所能搜索出的最远的点一定是直径的端点(也可采用反证法证明)

 

 

 

图中:ab是直径,cd是假定的答案(假定的最长偶权树链),lr是重叠部分;bl > cl , ra > rd (不满足的话c 或 d就是直径的端点了)

如果重叠部分lr为偶权,bl或ra为一偶一奇(按上图情况),那么cl和rd均为偶权,显然可以用bl去替换掉cl ,cd -> bd(按上图情况)

如果重叠部分lr为奇权,bl和ra均为偶权,那么cl和rd一个奇权 一个偶权,那么势必可以用bl 或者ar 对cl和rd中的偶权作替换;

证明完毕。

已知最长偶链端点一定是直径的某一端点,可以求出树的直径的两个端点,分别以这两个端点建树,用线段树维护其dfs序,每个点u维护的两个值,一个是根到u的奇权链长度,一个是根到u的偶权链长度,由于根到u的路径是唯一的,奇偶也是唯一的,所以偶权链或者奇权链的长度有一个为0,另一个则是u到根的距离。

  1 #include<bits/stdc++.h>
  2 /*
  3 #include<cstdio>
  4 #include<cmath>
  5 #include<cstring>
  6 #include<vector>
  7 #include<cctype>
  8 #include<queue>
  9 #include<algorithm>
 10 #include<map>
 11 #include<set>
 12 */
 13 //#pragma GCC optimize(2)
 14 using namespace std;
 15 typedef long long LL;
 16 typedef unsigned long long uLL;
 17 typedef pair<int,int> pii;
 18 typedef pair<LL,LL> pLL;
 19 typedef pair<double,double> pdd;
 20 const int N=5e5+5;
 21 const int M=8e5+5;
 22 const int inf=0x3f3f3f3f;
 23 const LL mod=1e8+7;
 24 const double eps=1e-8;
 25 const long double pi=acos(-1.0L);
 26 #define ls (i<<1)
 27 #define rs (i<<1|1)
 28 #define fi first
 29 #define se second
 30 #define pb push_back
 31 #define eb emplace_back
 32 #define mk make_pair
 33 #define mem(a,b) memset(a,b,sizeof(a))
 34 LL read()
 35 {
 36     LL x=0,t=1;
 37     char ch;
 38     while(!isdigit(ch=getchar())) if(ch=='-') t=-1;
 39     while(isdigit(ch)){ x=10*x+ch-'0'; ch=getchar(); }
 40     return x*t;
 41 
 42 }
 43 vector<pii> e[N];
 44 struct edge{int u,v,w;}a[N];
 45 int rt1,rt2,ma;
 46 pii ma1[N],ma2[N];
 47 //建两颗树(一棵树需要c、lz、int、out、rk、deep数组;如果直接用数组名传参,参数表就太长了,不如直接做一个结构体)
 48 struct node
 49 {
 50     int c[N<<2][2],lz[N<<2],in[N],out[N],rk[N],dep[N],col[N],tot;
 51     inline void pushdown(int i)
 52     {
 53         swap(c[ls][0],c[ls][1]);
 54         swap(c[rs][0],c[rs][1]);
 55         lz[ls]^=1; lz[rs]^=1;
 56         lz[i]=0;
 57     }
 58     inline void pushup(int i)
 59     {
 60         c[i][0]=max(c[ls][0],c[rs][0]);
 61         c[i][1]=max(c[ls][1],c[rs][1]);
 62     }
 63     void build(int i,int l,int r)
 64     {
 65         c[i][0]=c[i][1]=lz[i]=0;
 66         if(l==r) return (void)(c[i][col[rk[l]]]=dep[rk[l]]);
 67         int mid=l+r>>1;
 68         build(ls,l,mid);
 69         build(rs,mid+1,r);
 70         pushup(i);
 71     }
 72     void update(int i,int l,int r,int ll,int rr)
 73     {
 74         if(ll<=l&&r<=rr) return (void)(swap(c[i][0],c[i][1]),lz[i]^=1);
 75         int mid=l+r>>1;
 76         if(lz[i]) pushdown(i);
 77         if(mid>=ll) update(ls,l,mid,ll,rr);
 78         if(mid<rr) update(rs,mid+1,r,ll,rr);
 79         pushup(i);
 80     }
 81 
 82     void dfs(int u,int pre)
 83     {
 84         in[u]=++tot; rk[tot]=u;
 85         dep[u]=dep[pre]+1;
 86         for(auto x:e[u])
 87         {
 88             int v=x.fi,w=x.se;
 89             if(v==pre) continue;
 90             col[v]=col[u]^w;
 91             dfs(v,u);
 92         }
 93         out[u]=tot;
 94     }
 95 }tr[2];
 96 void dfs2(int u,int pre)
 97 {
 98     ma1[u]=ma2[u]=mk(0,u);
 99     for(auto x:e[u])
100     {
101         int v=x.fi;
102         if(v==pre) continue;
103         dfs2(v,u);
104         pii t=ma1[v];
105         if(t>ma1[u]) swap(t,ma1[u]);
106         if(t>ma2[u]) ma2[u]=t;
107         //f[u]=max(f[v],f[u]);
108     }
109     //printf("u = %d , fi = %d , se = %d\n",u,ma1[u].fi,ma2[u].fi);
110     if(ma1[u].fi+ma2[u].fi>ma) ma=ma1[u].fi+ma2[u].fi,rt1=ma1[u].se,rt2=ma2[u].se;
111     ma1[u].fi++; ma2[u].fi++;
112 }
113 
114 int main()
115 {
116     int n=read();
117     for(int i=1;i<n;i++)
118     {
119         int x=read(),y=read(),z=read();
120         a[i]=edge{x,y,z};
121         e[x].eb(y,z);
122         e[y].eb(x,z);
123     }
124     dfs2(1,0);
125    // printf("%d %d\n",rt1,rt2);
126     tr[0].dfs(rt1,0);
127     tr[0].build(1,1,n);
128     tr[1].dfs(rt2,0);
129     tr[1].build(1,1,n);
130     int m=read();
131     for(int i=1;i<=m;i++)
132     {
133         int x=read(),y=a[x].u;
134         if(tr[0].dep[a[x].u]<tr[0].dep[a[x].v]) y=a[x].v;
135         tr[0].update(1,1,n,tr[0].in[y],tr[0].out[y]);
136         y=a[x].u;
137         if(tr[1].dep[a[x].u]<tr[1].dep[a[x].v]) y=a[x].v;
138         tr[1].update(1,1,n,tr[1].in[y],tr[1].out[y]);
139         printf("%d\n",max(tr[0].c[1][0],tr[1].c[1][0])-1);
140     }
141     return 0;
142 }
View Code

 

每次翻转某一条边的权,实际上就是翻转整个子树所有点的权,对于单个点来说,就是奇权链变成偶权链,但是长度不变,只需要交换根到u 的奇权长度和偶权长度即可(延时标记处理)

代码:

 

posted @ 2020-10-29 17:10  DeepJay  阅读(208)  评论(0编辑  收藏  举报