洛谷 P5559 失昼城的守星使

Problem:

给定一个无向图,n个点,n-1条边,每个点有一个初始状态(标记/未标记),q个操作,每次操作修改一个点的状态(状态取反)/询问所有标记点到x->y的简单路径的距离之和。

Solution:

首先找到x->y的简单路径,然后求标记节点u到这条链的距离(u到LCA的距离减去u到LCA的路径与链的重合部分的长度),最后进行路径求和。

对于重合路径,对u->root的路径进行+1标记,然后每次查询链上路径和。

对于u->LCA的距离,有 dis(u,LCA)= dis(u,root)+ dis(LCA,root)- 2 * dis(lca(u,LCA))

Code:

  1 #include<bits/stdc++.h>
  2 using namespace std;
  3 
  4 #define ll long long
  5 const int N=2e5+7;
  6 
  7 int n,m,a[N];
  8 int dep[N];//节点深度
  9 int f[N];//父节点
 10 int son[N];//最大子节点
 11 int sz[N];//子树大小
 12 int top[N];//树链剖分后节点所在链的首节点 
 13 int num[N];//遍历次序 
 14 int LCA;
 15 int dfn=0;//记录遍历点个数 
 16 ll cnt=0;//标记点个数 
 17 ll siz[N];//子树中标记点个数 
 18 ll vis[N];//x->y简单路径不经过该节点时,节点子树中标记点接收信息在该路径上的能量消耗 
 19 ll pw[N];//连接父节点的边的边权 
 20 ll sw[N];//连接最大子节点的边的边权 
 21 ll tp;
 22 
 23 struct tree{
 24     ll w;
 25     ll sum;
 26     ll lazy;
 27 }t[N<<2];
 28 
 29 int head[N],tot=0;
 30 struct node{
 31     int nxt,to,val;
 32 }e[N<<1];
 33 //快读 
 34 inline int read(){
 35     int x=0,f=1;
 36     char ch=getchar();
 37     while(ch<'0'||ch>'9'){
 38     if(ch=='-') f=-1;
 39     ch=getchar();
 40     }
 41     while(ch>='0'&&ch<='9'){
 42     x=(x<<1)+(x<<3)+(ch^48);
 43     ch=getchar();
 44     }
 45     return x*f;
 46 }
 47 //建图 
 48 inline void add(int x,int y,int z){
 49     e[++tot].nxt=head[x];
 50     e[tot].to=y;
 51     e[tot].val=z;
 52     head[x]=tot;
 53     return ;
 54 }
 55 //预处理深度、父节点、子节点、标记、边权 
 56 inline void dfs1(int x,int fa){
 57     f[x]=fa;
 58     dep[x]=dep[fa]+1;
 59     siz[x]=a[x];
 60     sz[x]=1;
 61     for(int i=head[x];i;i=e[i].nxt){
 62         int y=e[i].to;
 63         if(y!=fa){
 64             dfs1(y,x);
 65         siz[x]+=siz[y];
 66         sz[x]+=sz[y];
 67             if(sz[son[x]]<sz[y]){
 68                 son[x]=y;
 69                sw[x]=e[i].val;
 70             }
 71         }
 72     }
 73     return ;
 74 }
 75 //预处理 
 76 inline void dfs2(int x,int tpx,ll ww){
 77     ++dfn;//遍历个数 
 78     num[x]=dfn;//x节点是第num[x]个遍历到的 
 79     vis[dfn]=ww*siz[x];//第dfn个遍历到的节点的子树中标记点个数*该节点到父节点的路径长度 
 80     pw[dfn]=ww;//第dfn个遍历到的节点到父节点的路径长度 
 81     top[x]=tpx;//x节点所在链的链首节点 
 82     if(son[x]) dfs2(son[x],tpx,sw[x]);//递归重链 
 83     for(int i=head[x];i;i=e[i].nxt){
 84         int y=e[i].to;
 85         int w=e[i].val;
 86         if(y!=son[x]&&y!=f[x]){
 87             dfs2(y,y,w);//递归轻链 
 88         }
 89     }
 90     return ;
 91 }
 92 //递归建树 
 93 inline void build(int l,int r,int x){
 94     if(l==r){
 95         t[x].sum=pw[l];
 96     t[x].w=vis[l]; 
 97     return;
 98     }
 99     int mid=(l+r)>>1;
100     build(l,mid,x<<1);//左子树 
101     build(mid+1,r,x<<1|1);//右子树 
102     t[x].w=t[x<<1].w+t[x<<1|1].w;
103     t[x].sum=t[x<<1].sum+t[x<<1|1].sum;
104     return ;
105 }
106 //更新 
107 inline void pushdown(int x){
108     if(t[x].lazy){
109         t[x<<1].lazy+=t[x].lazy;
110     t[x<<1|1].lazy+=t[x].lazy;
111     t[x<<1].w+=t[x<<1].sum*t[x].lazy;
112     t[x<<1|1].w+=t[x<<1|1].sum*t[x].lazy;
113     t[x].lazy=0;
114     }
115     return ;
116 }
117 
118 inline void update(int l,int r,int p,int q,int x,ll y){
119     if(p<=l&&r<=q){
120         t[x].lazy+=y;
121         t[x].w+=t[x].sum*y;
122     return;
123     }
124     int mid=l+r>>1;
125     pushdown(x);
126     if(p<=mid) update(l,mid,p,q,x<<1,y);
127     if(q>mid) update(mid+1,r,p,q,x<<1|1,y);
128     t[x].w=t[x<<1].w+t[x<<1|1].w;
129     return ;
130 }
131 //修改点的状态 
132 inline void updt(int x,int y){
133     int pp=a[y]?-1:1;//取反 
134     while(top[x]!=top[y]){
135         if(dep[top[x]]<dep[top[y]]) swap(x,y);
136         update(1,n,num[top[x]],num[x],1,pp);
137         x=f[top[x]];
138     }
139     if(dep[x]>dep[y]) swap(x,y);
140     update(1,n,num[x],num[y],1,pp);
141     return ; 
142 }
143 
144 inline ll qu1(int l,int r,int p,int q,int x){
145     if(p>q) return 0;
146     if(p<=l&&r<=q) return t[x].w;
147     int mid=l+r>>1;
148     ll ans=0;
149     pushdown(x);
150     if(p<=mid) ans=qu1(l,mid,p,q,x<<1);
151     if(q>mid) ans+=qu1(mid+1,r,p,q,x<<1|1);
152     return ans;
153 }
154 
155 inline ll qu2(int l,int r,int p,int q,int x){
156     if(p>q) return 0;
157     if(p<=l&&r<=q) return t[x].sum;
158     int mid=l+r>>1;
159     ll ans=0;
160     pushdown(x);
161     if(p<=mid) ans=qu2(l,mid,p,q,x<<1);
162     if(q>mid) ans+=qu2(mid+1,r,p,q,x<<1|1);
163     return ans;
164 }
165 
166 inline ll qu4(int x,int y){
167     ll ans=0;
168     while(top[x]!=top[y]){
169         if(dep[top[x]]<dep[top[y]]) swap(x,y);
170         ans+=qu2(1,n,num[top[x]],num[x],1);
171         x=f[top[x]];
172     }
173     if(dep[x]>dep[y]) swap(x,y);
174     ans+=qu2(1,n,num[x]+1,num[y],1);
175     return ans;
176 }
177 
178 inline ll qu3(int x,int y){
179     ll ans=0;
180     while(top[x]!=top[y]){
181         if(dep[top[x]]<dep[top[y]]) swap(x,y);
182         ans+=qu1(1,n,num[top[x]],num[x],1);
183         x=f[top[x]];
184     }
185     if(dep[x]>dep[y]) swap(x,y);
186     ans+=qu1(1,n,num[x]+1,num[y],1);
187     LCA=x;
188     return ans;
189 }
190 
191 inline ll find(int x,int y){
192     tp=qu3(x,y);
193     int w=LCA;
194     return t[1].w-tp-2ll*qu3(1,w)+cnt*qu4(1,w); 
195 }
196 
197 int main(){
198     n=read();
199     m=read();
200     tp=read();
201     for(int i=1;i<n;++i){
202         int u,v,w;
203         u=read();
204         v=read();
205         w=read();
206         add(u,v,w);
207         add(v,u,w);
208     }
209     for(int i=1;i<=n;++i) a[i]=read();
210     dfs1(1,0);
211     dfs2(1,1,0);
212     build(1,n,1);
213     cnt=siz[1];
214     for(int i=1;i<=m;++i){
215         int opt=read();
216     int u=read();
217         if(opt==1){
218             cnt=cnt+(a[u]?-1:1);//更新cnt 
219             updt(1,u);
220             a[u]=!a[u];//取反 
221         }
222         else{
223             int v=read();
224             printf("%lld\n",find(u,v));
225         }
226     }
227     return 0;
228 }
posted @ 2021-08-22 07:06  B_lank  阅读(95)  评论(0)    收藏  举报