[luogu]P3250 [HNOI2016]网络

原题链接:P3250 [HNOI2016]网络

题意

给定一棵无根树,m个操作
0操作:增加一条a到b的权值为v的链
1操作:删除第t个操作
2操作:输出不经过x节点的权值最大的链

分析

数据结构神题。。。

题目中要求维护不经过$x$节点的权值最大值,也就是说所有不经过$x$节点的链都能对答案产生贡献。

也就是说,当增加一条链的时,除了链上的点,都应该增加价值为$v$的权值,答案就转化为了$x$点上的最大权值。

我们可以线段树套堆……

每个区间开一个堆,堆内存这段区间的权值。

我们注意到堆需要支持删除操作。

如何支持删除呢,我们可以开一个删除标志,维护每一个堆需要开两个堆,一个堆用于存插入的数据,另一个堆是已经被删掉的数据。

如果两个堆顶一样,就一起$pop$掉,不然就输出第一个堆的堆顶。

如果第一个堆为空输出$-1$。 

由于堆的操作比较慢,而且插入和删除的操作区间是一样的,我这里直接标记永久化免去了下推标记操作。

代码

  1 #include <bits/stdc++.h>
  2 const int N=2*1e5+1009;
  3 using namespace std;
  4 int read(){
  5     char c;int num,f=1;
  6     while(c=getchar(),!isdigit(c))if(c=='-')f=-1;num=c-'0';
  7     while(c=getchar(), isdigit(c))num=num*10+c-'0';
  8     return f*num;
  9 }
 10 struct Heap{
 11     priority_queue<int>rl,dl;
 12     void push(int x){rl.push(x);}
 13     void del(int x){
 14         dl.push(x);
 15         while(!dl.empty()&&dl.top()==rl.top()){
 16             dl.pop();rl.pop();
 17         }
 18     }
 19     int top(){
 20         while(!dl.empty()&&dl.top()==rl.top()){
 21             dl.pop();rl.pop();
 22         }
 23         if(rl.empty())return -1;
 24         return rl.top();
 25     }
 26 }tree[N*5];
 27 struct link{
 28     int x,y,v;
 29 }lk[N],tmp[N];
 30 int n,m;
 31 int head[N],nxt[N*2],ver[N*2],tot=1;
 32 int son[N],fa[N],siz[N],id[N],dep[N],top[N],tott=0;
 33 void DFS1(int x,int pre,int deep){
 34     dep[x]=deep;siz[x]=1;
 35     for(int i=head[x];i;i=nxt[i]){
 36         if(ver[i]==pre)continue;
 37         DFS1(ver[i],x,deep+1);
 38         fa[ver[i]]=x;
 39         siz[x]+=siz[ver[i]];
 40         if(siz[ver[i]]>siz[son[x]])son[x]=ver[i];
 41     }
 42 }
 43 void DFS2(int x,int ltp){
 44     id[x]=++tott;
 45     top[x]=ltp;
 46     if(!son[x])return ;
 47     DFS2(son[x],ltp);
 48     for(int i=head[x];i;i=nxt[i]){
 49         if(ver[i]==fa[x]||ver[i]==son[x])continue;
 50         DFS2(ver[i],ver[i]);
 51     }
 52 }
 53 //树链剖分
 54 
 55 void modify(int l,int r,int L,int R,int rt,int opt,int xx){
 56     if(L<=l&&r<=R){
 57         opt?tree[rt].del(xx):tree[rt].push(xx);
 58         return ;
 59     }
 60     int mid=(l+r)>>1;
 61     if(L<=mid)modify(l,mid,L,R,rt<<1,opt,xx);
 62     if(mid< R)modify(mid+1,r,L,R,rt<<1|1,opt,xx);
 63 }
 64 int query(int l,int r,int x,int rt){
 65     if(l==r)return tree[rt].top();
 66     int mid=(l+r)>>1,ans=tree[rt].top();
 67     if(x<=mid)return max(ans,query(l,mid,x,rt<<1));
 68     else return max(ans,query(mid+1,r,x,rt<<1|1));
 69 }
 70 void add_edge(int u,int v){
 71     ver[++tot]=v;nxt[tot]=head[u];head[u]=tot;
 72     ver[++tot]=u;nxt[tot]=head[v];head[v]=tot;
 73 }
 74 void Swap(int &x,int &y){x^=y;y^=x;x^=y;}
 75 bool cmp(link x,link y){
 76     if(x.x!=y.x)return x.x<y.x;
 77     return x.y<y.y;
 78 }
 79 void link_modify(int u,int v,int val,int opt){
 80     int cnt=0;
 81     while(top[u]!=top[v]){
 82         if(dep[top[u]]<dep[top[v]])Swap(u,v);
 83         tmp[++cnt].x=id[top[u]];
 84         tmp[  cnt].y=id[u];
 85         u=fa[top[u]];
 86     }
 87     if(id[u]>id[v])Swap(u,v);
 88     tmp[++cnt].x=id[u];
 89     tmp[  cnt].y=id[v];
 90     sort(tmp+1,tmp+1+cnt,cmp);
 91     int maxn=0;
 92     for(int i=1;i<=cnt;maxn=max(maxn,tmp[i++].y))
 93         if(maxn+1<tmp[i].x)modify(1,n,maxn+1,tmp[i].x-1,1,opt,val);
 94     if(maxn<n)modify(1,n,maxn+1,n,1,opt,val);
 95 }
 96 int main()
 97 {
 98     n=read();m=read();
 99     for(int i=1;i< n;i++)add_edge(read(),read());
100     DFS1(1,1,1);DFS2(1,1);
101     for(int i=1;i<=m;i++){
102         int opt=read();
103         if(opt==0){
104             lk[i].x=read();
105             lk[i].y=read();
106             lk[i].v=read();
107             link_modify(lk[i].x,lk[i].y,lk[i].v,opt);
108         }else if(opt==1){
109             int ttt=read();
110             link_modify(lk[ttt].x,lk[ttt].y,lk[ttt].v,opt);
111         }else {
112             int a=read();
113             printf("%d\n",query(1,n,id[a],1));
114         }
115     }
116     return 0;
117 }
View Code

 

转载于:https://www.cnblogs.com/onglublog/p/10097316.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值