• [HNOI2016]网络


    Description:

    给定一棵n个点的树,有m次操作
    1.加入权值为w的一条链
    2.删除之前的一条链
    3.求不经过某个点的所有链中的最大权值

    Hint:

    (n le 10^5)

    Solution:

    暴力方法(nlog^3n)过掉了?

    首先考虑转化,一条链会对所有不在链上的点产生贡献 (这里没想到)

    每次暴力更新,最多(log)个区间

    由于要删除,我们可以用线段树套可删除堆

    所以修改的总复杂度是(nlog^3n)

    查询的话就单点查询,要考虑沿途答案

    #include <map>
    #include <set>
    #include <stack>
    #include <cmath>
    #include <queue>
    #include <cstdio>
    #include <cstring>
    #include <cstdlib>
    #include <iostream>
    #include <algorithm>
    #define ls p<<1 
    #define rs p<<1|1
    using namespace std;
    typedef long long ll;
    const int mxn=2e5+5;
    int n,m,df,cnt,tot,hd[mxn];
    int f[mxn],rk[mxn],sz[mxn],dep[mxn],dfn[mxn],top[mxn],son[mxn];
    
    inline int read() {
    	char c=getchar(); int x=0,f=1;
    	while(c>'9'||c<'0') {if(c=='-') f=-1;c=getchar();}
    	while(c<='9'&&c>='0') {x=(x<<3)+(x<<1)+(c&15);c=getchar();}
    	return x*f;
    }
    inline void chkmax(int &x,int y) {if(x<y) x=y;}
    inline void chkmin(int &x,int y) {if(x>y) x=y;}
    
    struct Tree {
    	priority_queue<int > q1,q2;
    	int top() {
    		while(!q1.empty()&&!q2.empty()&&q1.top()==q2.top())
    			q1.pop(),q2.pop();
    		if(q1.empty()) return -1;
    		else return q1.top();
    	}
    	void push(int x) {
    		q1.push(x);
    	}
    	void pop(int x) {
    		q2.push(x);
    	}
    }tr[mxn<<2];
    
    struct M {
    	int u,v,w;
    }mis[mxn];
    
    struct Q {
    	int l,r;
    }q[mxn];
    
    struct ed {
    	int to,nxt;
    }t[mxn<<1];
    
    int cmp(Q x,Q y) {
    	return x.l<y.l;
    }
    
    inline void add(int u,int v) {
    	t[++cnt]=(ed) {v,hd[u]}; hd[u]=cnt;
    }
    
    void dfs1(int u,int fa) 
    {
    	f[u]=fa; sz[u]=1; dep[u]=dep[fa]+1;
    	for(int i=hd[u];i;i=t[i].nxt) {
    		int v=t[i].to;
    		if(v==fa) continue ;
    		dfs1(v,u); sz[u]+=sz[v];
    		if(sz[son[u]]<sz[v]) son[u]=v;
    	}
    }
    
    void dfs2(int u,int tp)
    {
    	dfn[u]=++df; rk[df]=u; top[u]=tp;
    	if(son[u]) dfs2(son[u],tp);
    	for(int i=hd[u];i;i=t[i].nxt) {
    		int v=t[i].to;
    		if(v==f[u]||v==son[u]) continue ;
    		dfs2(v,v);
    	}
    }
    
    void update(int l,int r,int ql,int qr,int val,int opt,int p)
    {
    	if(qr<1||ql>n) return ;
    	if(ql<=l&&r<=qr) {
    		if(opt) tr[p].push(val);
    		else tr[p].pop(val);
    		return ;
    	}
    	int mid=(l+r)>>1;
    	if(ql<=mid) update(l,mid,ql,qr,val,opt,ls);
    	if(qr>mid) update(mid+1,r,ql,qr,val,opt,rs);
    }
    
    int query(int l,int r,int pos,int p)
    {
    	if(l==r) return tr[p].top();
    	int mid=(l+r)>>1;
    	if(pos<=mid) return max(tr[p].top(),query(l,mid,pos,ls));
    	else return max(tr[p].top(),query(mid+1,r,pos,rs));
    } 
    
    void modify(int x,int y,int w,int opt) 
    {
    	tot=0;
    	while(top[x]!=top[y]) {
    		if(dep[top[x]]<dep[top[y]]) swap(x,y);
    		q[++tot]={dfn[top[x]],dfn[x]};
    		x=f[top[x]];
    	}
    	if(dep[x]<dep[y]) swap(x,y);
    	q[++tot]=(Q){dfn[y],dfn[x]};
    	sort(q+1,q+tot+1,cmp);
    	update(1,n,1,q[1].l-1,w,opt,1);
    	for(int i=1;i<tot;++i) 
    		update(1,n,q[i].r+1,q[i+1].l-1,w,opt,1);
    	update(1,n,q[tot].r+1,n,w,opt,1);
    }
    
    int main()
    {
    	n=read(); m=read(); int u,v,w,opt;
    	for(int i=1;i<n;++i) {
    		u=read(); v=read();
    		add(u,v); add(v,u);
    	}
    	dfs1(1,0); dfs2(1,1); 
    	for(int i=1;i<=m;++i) {
    		opt=read();
    		if(opt==0) {
    			u=read(); v=read(); w=read();
    			mis[i]=(M){u,v,w}; modify(u,v,w,1);
    		}
    		else if(opt==1)
    			u=read(),modify(mis[u].u,mis[u].v,mis[u].w,0);
    		else {
    			u=read(); v=query(1,n,dfn[u],1);
    			printf("%d
    ",v);
    		}
    	}
        return 0;
    }
    
    
  • 相关阅读:
    随机的爱
    在使用ASP.NET时进行页面重定向的3种方法。(转)
    [程序员必看]请不要做浮躁的人
    C# datetime 操作
    心碎的浪漫
    动网论坛 企业版

    内存映射文件(File Mapping)API
    内存映射文件
    已知进程句柄,如何知道此进程的主窗口句柄
  • 原文地址:https://www.cnblogs.com/list1/p/10551601.html
Copyright © 2020-2023  润新知