dtoi4544「HNOI2016」网络
题意:
给定一棵树,每次有 $3$ 种操作
- 添加一条从 $u$ 到 $v$ 的重要度为 $d$ 的路径
- 删除一条以前添加的路径
- 询问当前时刻不经过点 $x$ 的路径中,重要度最大的路径的重要度
题解:
考虑对于一个添加或删除操作,可以树链剖分之后在经过的路径上打上重要度的标记,然后查询的时候就可以知道所有经过某个点的路径了。然而这样并没有什么用,显然要求的是这东西的补集。如果先求集合再求补集,是无法完成的,因为集合元素太多。此时考虑在添加操作的时候(删除操作同理),在不经过的点上打上重要度标记,然后查询就可以直接查询最大值。如何在不经过的点上打上标记?可以先树链剖分找出经过的点的区间,只有 $log$ 个,而且这些区间是互不重叠的。然后区间与区间的“间隙”就是不经过的点的区间,同样只有 $log$ 个。之后用线段树+multiset+标记永久化即可维护。
#include<cstdio> #include<vector> #include<set> #include<algorithm> #include<cstdlib> using namespace std; int n,m,fa[22][100002],tim,ta[200002],tb[200002],td[200002],len; multiset<int>q[400002]; vector<int>g[100002]; typedef struct{ int zjds,dfn,dep,zlt,xzd; }P; typedef struct{ int l,r; }PP; bool cmp(PP aa,PP bb){ return (aa.l<bb.l || aa.l==bb.l && aa.r<bb.r); } P p[100002]; PP t[100002]; int lca(int x,int y){ if (p[x].dep<p[y].dep)swap(x,y); int k=p[x].dep-p[y].dep; for (int i=0;i<=20;i++) if ((1<<i)&k)x=fa[i][x]; if (x==y)return x; for (int i=20;i>=0;i--) if (fa[i][x]!=fa[i][y]) { x=fa[i][x];y=fa[i][y]; } return fa[0][x]; } void dfs1(int x,int y){ int Max=0,maxn=-1; p[x].zjds=1;p[x].dep=p[y].dep+1;fa[0][x]=y; for (int i=0;i<g[x].size();i++) if (g[x][i]!=y) { dfs1(g[x][i],x);p[x].zjds+=p[g[x][i]].zjds; if (p[g[x][i]].zjds>Max) { Max=p[g[x][i]].zjds;maxn=g[x][i]; } } p[x].xzd=maxn; } void dfs2(int x,int y){ p[x].dfn=++tim; if (p[y].xzd==x)p[x].zlt=p[y].zlt;else p[x].zlt=x; if (p[x].xzd!=-1)dfs2(p[x].xzd,x); for (int i=0;i<g[x].size();i++) if (g[x][i]!=p[x].xzd && g[x][i]!=y)dfs2(g[x][i],x); } void gengxin(int root,int begin,int end,int begin2,int end2,int z){ if (begin>end2 || end<begin2)return; if (begin>=begin2 && end<=end2) { if (z<0) { multiset<int>::iterator it=q[root].find(-z); q[root].erase(it); } else q[root].insert(z); return; } int mid=(begin+end)/2; gengxin(root*2,begin,mid,begin2,end2,z);gengxin(root*2+1,mid+1,end,begin2,end2,z); } int chaxun(int root,int begin,int end,int wz){ if (begin==end) { if (q[root].empty())return -1; multiset<int>::iterator it=q[root].end();it--; return (*it); } int mid=(begin+end)/2; if (wz<=mid) { int ans=-1; if (!q[root].empty()) { multiset<int>::iterator it=q[root].end();it--; ans=*it; } return max(ans,chaxun(root*2,begin,mid,wz)); } else { int ans=-1; if (!q[root].empty()) { multiset<int>::iterator it=q[root].end();it--; ans=*it; } return max(ans,chaxun(root*2+1,mid+1,end,wz)); } } void work(int u,int v,int d){ int lc=lca(u,v);len=0; while(p[u].dep>=p[lc].dep) { if (p[p[u].zlt].dep>=p[lc].dep) { t[++len].l=p[p[u].zlt].dfn;t[len].r=p[u].dfn; } else { t[++len].l=p[lc].dfn;t[len].r=p[u].dfn; } u=fa[0][p[u].zlt]; } while(p[v].dep>=p[lc].dep) { if (p[p[v].zlt].dep>=p[lc].dep) { t[++len].l=p[p[v].zlt].dfn;t[len].r=p[v].dfn; } else { t[++len].l=p[lc].dfn;t[len].r=p[v].dfn; } v=fa[0][p[v].zlt]; } sort(t+1,t+len+1,cmp); for (int i=2;i<=len;i++) if (t[i].l-1>=t[i-1].r+1)gengxin(1,1,n,t[i-1].r+1,t[i].l-1,d); if (t[1].l>1)gengxin(1,1,n,1,t[1].l-1,d); if (t[len].r<n)gengxin(1,1,n,t[len].r+1,n,d); } int main() { scanf("%d%d",&n,&m); for (int i=1;i<n;i++) { int u,v; scanf("%d%d",&u,&v); g[u].push_back(v);g[v].push_back(u); } dfs1(1,0);dfs2(1,0); for (int i=1;i<=20;i++) for (int j=1;j<=n;j++) fa[i][j]=fa[i-1][fa[i-1][j]]; for (int i=1;i<=m;i++) { int op; scanf("%d",&op); if (op==0) { scanf("%d%d%d",&ta[i],&tb[i],&td[i]); work(ta[i],tb[i],td[i]); } else if (op==1) { int sk;scanf("%d",&sk); work(ta[sk],tb[sk],-td[sk]); } else { int x;scanf("%d",&x); printf("%d\n",chaxun(1,1,n,p[x].dfn)); } } return 0; }