【BZOJ】1984 月下“毛景树”
【算法】树链剖分+线段树
【题解】线段树的区间加值和区间覆盖操作不能同时存在,只能存在一个。
修改:从根节点跑到目标区域路上的标记全部下传,打完标记再上传回根节点(有变动才需要上传)。
询问:访问到目标区域路上的标记全部下传。
我写的线段树版本是在打标记的同时便对该点的询问项(最大值)做了对应更改,即可保证访问到该点得到的ms就是该点的答案。
访问某点时如果要询问最大值就直接拿走,如果要还要访问该点的子节点就需要下传。
而修改了某点的值,它的祖先的值就都需要变动,所以一旦修改必须上传至顶。
对于add和cover不共存的问题,下传过程:
void pushdown(int k) { if(t[k].l==t[k].r)return; if(t[k].c!=-1) { t[k<<1].a=t[k<<1|1].a=0; t[k<<1].c=t[k<<1|1].c=t[k].c; t[k<<1].ms=t[k<<1|1].ms=t[k].c; t[k].c=-1; } if(t[k].a!=0) { if(t[k<<1].c!=-1)t[k<<1].c+=t[k].a; else t[k<<1].a+=t[k].a; if(t[k<<1|1].c!=-1)t[k<<1|1].c+=t[k].a; else t[k<<1|1].a+=t[k].a; t[k<<1].ms+=t[k].a;t[k<<1|1].ms+=t[k].a; t[k].a=0; } }
下传cover时,子树add'=0,cover'=cover,ms=cover
下传add时,就要看子树的标记是cover还是add了。
边权赋给下面的点,注意LCA不算(树剖过程中顺便处理即可,不必写倍增)
不在同一条重链时,是deep[top[u]]大的先,不是deep[u]。
#include<cstdio> #include<algorithm> using namespace std; const int maxn=200010,inf=0x3f3f3f3f; struct tree{int l,r,c,a,ms;}t[maxn*3]; struct edge{int v,w,from;}e[maxn*3]; int dfsnum,n,pos[maxn],size[maxn],first[maxn],deep[maxn],f[maxn],id[maxn*3],tot,top[maxn]; void insert(int u,int v,int w) {tot++;e[tot].v=v;e[tot].w=w;e[tot].from=first[u];first[u]=tot;} void build(int k,int l,int r) { t[k].l=l;t[k].r=r;t[k].c=-1; if(l!=r) { int mid=(l+r)>>1; build(k<<1,l,mid); build(k<<1|1,mid+1,r); } } void pushdown(int k) { if(t[k].l==t[k].r)return; if(t[k].c!=-1) { t[k<<1].a=t[k<<1|1].a=0; t[k<<1].c=t[k<<1|1].c=t[k].c; t[k<<1].ms=t[k<<1|1].ms=t[k].c; t[k].c=-1; } if(t[k].a!=0) { if(t[k<<1].c!=-1)t[k<<1].c+=t[k].a; else t[k<<1].a+=t[k].a; if(t[k<<1|1].c!=-1)t[k<<1|1].c+=t[k].a; else t[k<<1|1].a+=t[k].a; t[k<<1].ms+=t[k].a;t[k<<1|1].ms+=t[k].a; t[k].a=0; } } void pushup(int k) { t[k].ms=max(t[k<<1].ms,t[k<<1|1].ms); } void cover(int k,int l,int r,int x) { int left=t[k].l,right=t[k].r; pushdown(k); if(l<=left&&right<=r)t[k].c=x,t[k].ms=x; else { int mid=(left+right)>>1; if(l<=mid)cover(k<<1,l,r,x); if(r>mid)cover(k<<1|1,l,r,x); pushup(k); } } void add(int k,int l,int r,int x) { int left=t[k].l,right=t[k].r; pushdown(k); if(l<=left&&right<=r)t[k].a+=x,t[k].ms+=x; else { int mid=(left+right)>>1; if(l<=mid)add(k<<1,l,r,x); if(r>mid)add(k<<1|1,l,r,x); pushup(k); } } int ask(int k,int l,int r) { int left=t[k].l,right=t[k].r; if(l<=left&&right<=r)return t[k].ms; else { int mid=(left+right)>>1; pushdown(k);int mss=-inf; if(l<=mid)mss=ask(k<<1,l,r); if(r>mid)mss=max(mss,ask(k<<1|1,l,r)); return mss; } } void solve_cover() { int u,v,w; scanf("%d%d%d",&u,&v,&w); while(top[u]!=top[v]) { if(deep[top[u]]<deep[top[v]])swap(u,v); cover(1,pos[top[u]],pos[u],w); u=f[top[u]]; } if(pos[u]>pos[v])swap(u,v); if(pos[u]<pos[v])cover(1,pos[u]+1,pos[v],w); } void solve_add() { int u,v,w; scanf("%d%d%d",&u,&v,&w); while(top[u]!=top[v]) { if(deep[top[u]]<deep[top[v]])swap(u,v); add(1,pos[top[u]],pos[u],w); u=f[top[u]]; } if(pos[u]>pos[v])swap(u,v); if(pos[u]<pos[v])add(1,pos[u]+1,pos[v],w); } void ask_max() { int u,v; scanf("%d%d",&u,&v); int maxs=-inf; while(top[u]!=top[v]) { if(deep[top[u]]<deep[top[v]])swap(u,v); maxs=max(maxs,ask(1,pos[top[u]],pos[u])); u=f[top[u]]; } if(pos[u]>pos[v])swap(u,v); if(pos[u]<pos[v])maxs=max(maxs,ask(1,pos[u]+1,pos[v])); printf("%d\n",maxs); } void dfs1(int x,int fa) { size[x]=1; for(int i=first[x];i;i=e[i].from) if(e[i].v!=fa) { int y=e[i].v; deep[y]=deep[x]+1; f[y]=x; dfs1(y,x); size[x]+=size[y]; } } void dfs2(int x,int tp,int fa) { pos[x]=++dfsnum; top[x]=tp; int k=0; for(int i=first[x];i;i=e[i].from) if(e[i].v!=fa){if(size[e[i].v]>size[k])k=e[i].v;} else id[(i+1)>>1]=x,add(1,pos[x],pos[x],e[i].w); if(k==0)return;//!!! dfs2(k,tp,x); for(int i=first[x];i;i=e[i].from) if(e[i].v!=fa&&e[i].v!=k)dfs2(e[i].v,e[i].v,x); } int main() { scanf("%d",&n); for(int i=1;i<n;i++) { int u,v,w; scanf("%d%d%d",&u,&v,&w); insert(u,v,w); insert(v,u,w); } build(1,1,n); dfs1(1,-1); dfs2(1,1,-1); char st[10];int u,v; for(;;) { scanf("%s",st); if(st[1]=='h')//change { scanf("%d%d",&u,&v); cover(1,pos[id[u]],pos[id[u]],v); } if(st[1]=='o')solve_cover();//cover if(st[1]=='d')solve_add();//add if(st[1]=='a')ask_max();//max if(st[1]=='t')break;//stop } return 0; }