题目描述:
摆在 H 君和 R 君面前的是一棵圣诞树。圣诞节就要到了,H 君和 R 君想在圣诞树上挂礼物。
圣诞树上本来是空的,即每个节点都没有礼物。
H 君每次会给你三个值 (u,v,w) 表示他想知道 u到 v的路径上所有值是偶数的数字之和,在这之后,他会将 u到 v的路径上所有值是奇数的数加上 w。
R 君每次会给你三个值 (u,v,w) 表示她想知道 u 到 v 的路径上所有值是奇数的数字之和,在这之后,她会将 u 到 v 的路径上所有值是偶数的数加上 w。
H 君和 R 君忙着挂礼物,就只能让你来回答他们的疑问了。
输入描述:
第一行两个整数 n和 m,表示树的节点个数和操作总数。
第二行到第 n行,每行两个整数 u,v,表示添加一条点 u到点 v 的边。
接下来 m 行,每行四个整数 op,u,v,w。如果 op=1表示这是一次 H 君的询问,如果 op = 2表示这是一次 R 君的询问。
输出描述:
共 m行,每行一个整数表示第 i次操作的答案。
数据:
对所有数据保证:1<=n<=1.6*10^5, 1<=m<=10^5,op∈1,2, 1<=u,v<=n, 0<=w<=20 。
比赛的时候能想到是树链剖分,但是懒标记的处理的不好,因为是io赛制,最后发现只拿了5分。然后当晚改来改去,隔天早上才把懒标记处理好了。
思路:
题目的要求就是分开处理奇偶数,但是难点在于,奇数在加上一个奇数值就会变成偶数,偶数同理。所以需要维护的是奇偶数的信息,以及在add操作的时候判断当前修改的对象 究竟是奇数还是偶数。
我的线段树是{l,r,on,oe,sumo,sume,lao,lae}
分别表示{左子树,右子树,区间奇数值结点个数,区间偶数值节点的个数,区间奇数值总和,区间偶数值总和,修改于奇数值节点的懒标记,修改于偶数值节点的懒标记}
我一开始的无脑的错误是:判断当前修改对象的奇偶性时(包括add操作和down操作),分开考虑sumo和sume,然后各自操作,以及分开考虑lao和lae的传递。
而正确做法就是综合考虑,用lazy和lazy传递对象的奇偶性,判断该对象的当前奇偶性,然后综合起来一起操作。
哎,主要还是得想清楚。
#include<bits/stdc++.h> #define debug printf("!"); using namespace std; typedef long long ll; const int maxn=6e5+5; vector<int>p[maxn]; int siz[maxn],dep[maxn],fad[maxn],son[maxn]; int top[maxn],tid[maxn],rnk[maxn],cnt; inline void dfs1(int u,int fa,int d) { dep[u]=d; fad[u]=fa; siz[u]=1; for(int i=0;i<p[u].size();i++) { int v=p[u][i]; if(v==fa)continue; dfs1(v,u,d+1); siz[u]+=siz[v]; if(siz[v]>siz[son[u]])son[u]=v; } } inline void dfs2(int u,int t) { top[u]=t; tid[u]=++cnt; rnk[cnt]=u; if(!son[u])return;//当前节点不在重链上 dfs2(son[u],t); for(int i=0;i<p[u].size();i++) { int v=p[u][i]; if(v!=son[u]&&v!=fad[u])dfs2(v,v);//非重节点的top是自己 } } struct kkk{ int l,r; int on,en; ll sumo,sume; int lao,lae; }tree[maxn<<2]; inline void build(int k,int l,int r) { tree[k].l=l;tree[k].r=r; tree[k].sumo=tree[k].sume=tree[k].lao=tree[k].lae=tree[k].en=tree[k].on=0; if(l==r) { tree[k].en=1; return; } int mid=(l+r)>>1; build(k<<1,l,mid); build(k<<1|1,mid+1,r); tree[k].en=tree[k<<1].en+tree[k<<1|1].en; } inline void down(int k) { tree[k<<1].sumo+=tree[k<<1].on*tree[k].lao; tree[k<<1].sume+=tree[k<<1].en*tree[k].lae; tree[k<<1|1].sumo+=tree[k<<1|1].on*tree[k].lao; tree[k<<1|1].sume+=tree[k<<1|1].en*+tree[k].lae; if(tree[k].lao%2==1&&tree[k].lae%2==1) { swap(tree[k<<1].sumo,tree[k<<1].sume); swap(tree[k<<1].on,tree[k<<1].en); swap(tree[k<<1|1].sumo,tree[k<<1|1].sume); swap(tree[k<<1|1].on,tree[k<<1|1].en); } else if(tree[k].lao&1)//sumo+odd=even { tree[k<<1].sume+=tree[k<<1].sumo;tree[k<<1].sumo=0; tree[k<<1].en+=tree[k<<1].on;tree[k<<1].on=0; tree[k<<1|1].sume+=tree[k<<1|1].sumo;tree[k<<1|1].sumo=0; tree[k<<1|1].en+=tree[k<<1|1].on;tree[k<<1|1].on=0; } else if(tree[k].lae&1) { tree[k<<1].sumo+=tree[k<<1].sume;tree[k<<1].sume=0; tree[k<<1].on+=tree[k<<1].en;tree[k<<1].en=0; tree[k<<1|1].sumo+=tree[k<<1|1].sume;tree[k<<1|1].sume=0; tree[k<<1|1].on+=tree[k<<1|1].en;tree[k<<1|1].en=0; } if(tree[k<<1].lao%2==0)tree[k<<1].lao+=tree[k].lao; else tree[k<<1].lao+=tree[k].lae; if(tree[k<<1].lae%2==1)tree[k<<1].lae+=tree[k].lao; else tree[k<<1].lae+=tree[k].lae; if(tree[k<<1|1].lao%2==0)tree[k<<1|1].lao+=tree[k].lao; else tree[k<<1|1].lao+=tree[k].lae; if(tree[k<<1|1].lae%2==1)tree[k<<1|1].lae+=tree[k].lao; else tree[k<<1|1].lae+=tree[k].lae; tree[k].lao=tree[k].lae=0; } inline void add(int k,int l,int r,int w,bool iso) { if(l<=tree[k].l&&tree[k].r<=r) { if(iso)//odd { if(tree[k].lao%2==0&&tree[k].lae%2==1)tree[k].lao+=w,tree[k].lae+=w; else if(tree[k].lao%2==0)tree[k].lao+=w; else if(tree[k].lae%2==1)tree[k].lae+=w; tree[k].sumo+=tree[k].on*w; if(w&1)//odd+odd=even { tree[k].sume+=tree[k].sumo;tree[k].sumo=0; tree[k].en+=tree[k].on;tree[k].on=0; } } else { if(tree[k].lao%2==1&&tree[k].lae%2==0)tree[k].lao+=w,tree[k].lae+=w; else if(tree[k].lao%2==1)tree[k].lao+=w; else if(tree[k].lae%2==0)tree[k].lae+=w; tree[k].sume+=tree[k].en*w; if(w&1) { tree[k].sumo+=tree[k].sume;tree[k].sume=0; tree[k].on+=tree[k].en;tree[k].en=0; } } return; } if(tree[k].lao||tree[k].lae)down(k); int mid=(tree[k].l+tree[k].r)>>1; if(l<=mid)add(k<<1,l,r,w,iso); if(r>mid)add(k<<1|1,l,r,w,iso); tree[k].sumo=tree[k<<1].sumo+tree[k<<1|1].sumo; tree[k].sume=tree[k<<1].sume+tree[k<<1|1].sume; tree[k].on=tree[k<<1].on+tree[k<<1|1].on; tree[k].en=tree[k<<1].en+tree[k<<1|1].en; } inline ll query(int k,int l,int r,int iso) { if(l<=tree[k].l&&tree[k].r<=r) { if(iso)return tree[k].sumo; else return tree[k].sume; } if(tree[k].lao||tree[k].lae)down(k); int mid=(tree[k].l+tree[k].r)>>1; ll ans=0; if(l<=mid)ans+=query(k<<1,l,r,iso); if(r>mid)ans+=query(k<<1|1,l,r,iso); return ans; } inline void addline(int x,int y,int w,bool iso) { while(top[x]!=top[y]) { if(dep[top[x]]<dep[top[y]])swap(x,y); add(1,tid[top[x]],tid[x],w,iso); x=fad[top[x]]; } if(dep[x]>dep[y])swap(x,y); add(1,tid[x],tid[y],w,iso); } inline ll calline(int x,int y,bool iso) { ll ans=0; while(top[x]!=top[y]) { if(dep[top[x]]<dep[top[y]])swap(x,y); ans+=query(1,tid[top[x]],tid[x],iso); x=fad[top[x]]; } if(dep[x]>dep[y])swap(x,y); ans+=query(1,tid[x],tid[y],iso); return ans; } int main() { int n,m,u,v,w,op; scanf("%d%d",&n,&m); for(int i=1;i<n;i++) { scanf("%d%d",&u,&v); p[u].push_back(v); p[v].push_back(u); } memset(son,0,sizeof(son)); dfs1(1,0,1); dfs2(1,1); build(1,1,n); while(m--) { scanf("%d%d%d%d",&op,&u,&v,&w); if(op==1) { printf("%lld ",calline(u,v,0)); addline(u,v,w,1); } else { printf("%lld ",calline(u,v,1)); addline(u,v,w,0); } } }