题解 P4315 【月下“毛景树”】

Juan_feng

2018-08-29 18:39:16

Solution

### 总算是A掉这道题了qwq 真是把小蒟蒻我给榨干了qwq A掉这道题经过了两天多的努力和一些神奇的经历(有兴趣可以自行观看讨论 **咳咳,进入正题** **这道题其实就是一个裸的边权树剖,不会树剖的可以右转P3384树剖模板** 小蒟蒻一开始很快想到思路就来做这道题,但做完了就发现到处都是问题(果然还是我太菜了qwq),其实这道题的细节部分好像也不是很复杂,稍微注意一下就好了qwq #### 有几点需要注意: 1:树剖维护的是边权,可以**把边权转化到点权上**。 怎么转化呢?因为每个孩子节点通向父节点的边是唯一的,所以可以将每个边的边权转到边所连的孩子节点上(可在树剖的第一个dfs中完成) 2:修改一条链上的权值时,要注意链两端的点的**lca不能够被修改**,因为lca所对应的边权不在这一条链上。 3:Change 操作是修改**第k条**树枝,**k为读入的顺序**,而树的存边是双向的,所以要将读入的k乘以二在进行后面的操作。 4:下推标记的时候如果有覆盖标记不要忘了**清除加的标记**。 上面几点有的之前的dalao已经说过了,权当是再做一次总结 注意了以上几点,程序自然就不在话下了qwq 感谢@rehtorbegnaro以及其他dalao的帮助和支持 **献上233行代码**233 ``` #include<iostream> #include<cstring> #include<cstdio> #include<algorithm> #define maxn 2000010 #define ll long long #define re register #define FOR(i,l,r) for(re ll i=l;i<=r;i++) using namespace std; ll tag1[maxn<<2],tag2[maxn<<2],ans[maxn<<2],head[maxn<<1],a[maxn];//tag1为覆盖,tag2为加 ll top[maxn],son[maxn],depth[maxn],fa[maxn],siz[maxn],id[maxn],dd[maxn]; ll n,m,k,t,num,cnt,x,y,z,w,q; string s; struct hz{ ll next; ll to; ll dis; ll from; }h[maxn<<1]; inline void in(ll &x){ //快读使我快乐 x=0;ll f=1;char c=getchar(); while(c<'0'||c>'9'){ if(c==-1) return; if(c=='-')f=-1; c=getchar(); } while(c<='9'&&c>='0'){ x=(x<<1)+(x<<3)+(c^'0'); c=getchar(); } x=x*f; } inline void out(ll a){ if(a<0){ a*=-1; putchar('-'); } if(a>=10)out(a/10); putchar(a%10+'0'); } inline void add(ll from,ll to,ll dis){ //邻接表存边 h[++num].next=head[from]; h[num].to=to; h[num].from=from; h[num].dis=dis; head[from]=num; } //------------------------------以下为线段树部分 inline ll ls(ll k){ return k<<1; } inline ll rs(ll k){ return k<<1|1; } inline void push_up(ll k){ ans[k]=max(ans[ls(k)],ans[rs(k)]); } inline void push_down(ll p){ //下推标记 if(tag1[p]!=-1){ //下推覆盖标记时要清楚加标记 tag1[ls(p)]=tag1[rs(p)]=tag1[p]; ans[ls(p)]=ans[rs(p)]=tag1[p]; tag2[ls(p)]=tag2[rs(p)]=0; tag1[p]=-1; } tag2[ls(p)]+=tag2[p]; tag2[rs(p)]+=tag2[p]; ans[ls(p)]+=tag2[p]; ans[rs(p)]+=tag2[p]; tag2[p]=0; } void update1(ll nl,ll nr,ll l,ll r,ll p,ll k){ //区间覆盖 if(nl<=l&&r<=nr){ ans[p]=k; tag1[p]=k; tag2[p]=0; return; } ll mid=(l+r)>>1; push_down(p); if(nl<=mid) update1(nl,nr,l,mid,ls(p),k); if(nr>mid) update1(nl,nr,mid+1,r,rs(p),k); push_up(p); } void update2(ll nl,ll nr,ll l,ll r,ll p,ll k){ //区间加 if(nl<=l&&r<=nr){ ans[p]+=k; tag2[p]+=k; return; } ll mid=(l+r)>>1; push_down(p); if(nl<=mid) update2(nl,nr,l,mid,ls(p),k); if(nr>mid) update2(nl,nr,mid+1,r,rs(p),k); push_up(p); } ll query(ll q_x,ll q_y,ll l,ll r,ll p){ //区间求Max if(q_x<=l&&r<=q_y) return ans[p]; ll res=0; ll mid=(l+r)>>1; push_down(p); if(q_x<=mid) res=max(res,query(q_x,q_y,l,mid,ls(p))); if(q_y>mid) res=max(res,query(q_x,q_y,mid+1,r,rs(p))); return res; } void build(ll p,ll l,ll r){ //建树 tag1[p]=-1; tag2[p]=0; if(l==r){ ans[p]=dd[l]; return; } ll mid=(l+r)>>1; build(ls(p),l,mid); build(rs(p),mid+1,r); push_up(p); } //------------------------------以上为线段树部分 void dfs1(ll f,ll ff){ depth[f]=depth[ff]+1; fa[f]=ff; siz[f]=1; ll maxson=-1; for(re ll i=head[f];i!=0;i=h[i].next){ if(h[i].to==ff) continue; dfs1(h[i].to,f); a[h[i].to]=h[i].dis; //把边权化为点权 siz[f]+=siz[h[i].to]; if(siz[h[i].to]>maxson){ maxson=siz[h[i].to]; son[f]=h[i].to; } } } void dfs2(ll x,ll topf){ top[x]=topf; id[x]=++cnt; dd[cnt]=a[x]; if(!son[x]) return; dfs2(son[x],topf); for(re ll i=head[x];i!=0;i=h[i].next){ if(h[i].to==fa[x]||h[i].to==son[x]) continue; dfs2(h[i].to,h[i].to); } } void updrange1(ll x,ll y,ll k){ //Change和Cover操作 while(top[x]!=top[y]){ if(depth[top[x]]<depth[top[y]]) swap(x,y); update1(id[top[x]],id[x],1,n,1,k); x=fa[top[x]]; } if(depth[x]>depth[y]) swap(x,y); update1(id[x]+1,id[y],1,n,1,k); //不能更新lca所以是(id[x]+1) } void updrange2(ll x,ll y,ll k){ //Add操作 while(top[x]!=top[y]){ if(depth[top[x]]<depth[top[y]]) swap(x,y); update2(id[top[x]],id[x],1,n,1,k); x=fa[top[x]]; } if(depth[x]>depth[y]) swap(x,y); update2(id[x]+1,id[y],1,n,1,k); //同上 } ll qrange(ll x,ll y){ //Max操作 ll anss=0; while(top[x]!=top[y]){ if(depth[top[x]]<depth[top[y]]) swap(x,y); anss=max(anss,query(id[top[x]],id[x],1,n,1)); x=fa[top[x]]; } if(depth[x]>depth[y]) swap(x,y); return max(anss,query(id[x]+1,id[y],1,n,1));//同理 } int main(){ in(n); FOR(i,1,n-1){ in(x),in(y),in(z); add(x,y,z); add(y,x,z); //双向加边 } dfs1(1,0); dfs2(1,1); build(1,1,n); while(s!="Stop"){ if(s[1]=='h'){ in(x),in(k); x*=2; //因为边有重复的,所以乘以二保证是第K条边 x=depth[h[x].from]>depth[h[x].to]?h[x].from:h[x].to;//深度大的点(子节点)保存权值 update1(id[x],id[x],1,n,1,k); //Change操作 } if(s[1]=='o'){ in(x),in(y),in(z); updrange1(x,y,z); //Cover操作 } if(s[1]=='d'){ in(x),in(y),in(z); updrange2(x,y,z); //Add操作 } if(s[1]=='a'){ in(x),in(y); out(qrange(x,y)); //Max操作 puts(""); } cin>>s; } return 0; //功德圆满 } ```