zoukankan      html  css  js  c++  java
  • [CTSC2008] 网络管理

    一、题目

    点此看题

    二、解法

    一个比较显然的做法的树剖维护树套树,时间复杂度 (O(nlog^3n))

    现在讲一下 (O(nlog^2 n)) 的做法,首先考虑不带修改怎么做,每个点维护到根的权值线段树,然后直接拿 (u,v,lca,fa[lca]) 这四个根在线段树上二分即可,用主席树优化一下就可以做到 (O(nlog n))

    但是这道题是单点修改的,主席树带修之后变成了树套树,我们可以在外层套一个以 (dfn) 序为下标的树状数组,修改就修改子树内所有的权值线段树,也就是对于一段连续的区间进行修改。那么就维护一棵元素为权值线段树的差分树状数组,就是区间修改单点查询的经典问题了,询问的时候就带着 (O(log n)) 个根去线段树二分即可,时间复杂度 (O(nlog^2 n))

    #include <cstdio>
    #include <vector>
    #include <iostream>
    using namespace std;
    const int M = 80005;
    const int up = 1e8;
    #define pii pair<int,int>
    #define make make_pair 
    int read()
    {
    	int x=0,f=1;char c;
    	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
    	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
    	return x*f;
    }
    int n,q,tot,Ind,f[M],a[M],fa[M][20],dfn[M],dfo[M],dep[M];
    int cnt,rt[M],sum[200*M],ls[200*M],rs[200*M];
    vector<pii> v;//询问用的根集合 
    //rt[i]表示树状数组节点对应线段树的根
    //sum[i]表示线段树的子树和(出现次数)
    struct edge
    {
    	int v,next;
    	edge(int V=0,int N=0) : v(V) , next(N) {}
    }e[2*M];
    void dfs(int u)//预处理,算dfs序
    {
    	dfn[u]=++Ind;
    	dep[u]=dep[fa[u][0]]+1;
    	for(int i=1;i<20;i++)
    		fa[u][i]=fa[fa[u][i-1]][i-1];
    	for(int i=f[u];i;i=e[i].next)
    	{
    		int v=e[i].v;
    		if(v==fa[u][0]) continue;
    		fa[v][0]=u;
    		dfs(v);
    	}
    	dfo[u]=Ind;
    }
    int lca(int u,int v)
    {
    	if(dep[u]<dep[v]) swap(u,v);
    	for(int i=19;i>=0;i--)
    		if(dep[fa[u][i]]>=dep[v])
    			u=fa[u][i];
    	if(u==v) return u;
    	for(int i=19;i>=0;i--)
    		if(fa[u][i]^fa[v][i])
    			u=fa[u][i],v=fa[v][i];
    	return fa[u][0];
    }
    int lowbit(int x)
    {
    	return x&(-x);
    }
    void ins(int &x,int l,int r,int y,int f)//权值线段树的插入
    {
    	if(!x) x=++cnt;
    	sum[x]+=f;
    	if(l==r) return ;
    	int mid=(l+r)>>1;
    	if(mid>=y) ins(ls[x],l,mid,y,f);
    	else ins(rs[x],mid+1,r,y,f);
    }
    void upd(int x,int y,int f)
    //修改dfn序为x位置的差分值,插入权值y 
    {
    	while(x<=n)
    	{
    		ins(rt[x],1,up,y,f);
    		x+=lowbit(x);
    	}
    }
    void ask(int x,int f)
    //其实这里是取出差分前缀x的所有根,并且带上正负标记 
    {
    	while(x>0)
    	{
    		v.push_back(make(rt[x],f)); 
    		x-=lowbit(x);
    	}
    }
    int qry(int l,int r,int k)//值域区间[l,r]的第k大 
    {
    	int t=0;
    	for(int i=0;i<v.size();i++)
    		t+=v[i].second*sum[rs[v[i].first]];
    	if(l==r) return l;
    	int mid=(l+r)>>1;
    	if(t>=k)//分到右边 
    	{
    		for(int i=0;i<v.size();i++)
    			v[i].first=rs[v[i].first];
    		return qry(mid+1,r,k);
    	}
    	k-=t;//减掉之后分左边 
    	for(int i=0;i<v.size();i++)
    		v[i].first=ls[v[i].first];
    	return qry(l,mid,k);
    }
    signed main()
    {
    	n=read();q=read();
    	for(int i=1;i<=n;i++)
    		a[i]=read();
    	for(int i=1;i<n;i++)
    	{
    		int u=read(),v=read();
    		e[++tot]=edge(v,f[u]),f[u]=tot;
    		e[++tot]=edge(u,f[v]),f[v]=tot;
    	}
    	dfs(1);
    	for(int i=1;i<=n;i++)
    	{
    		upd(dfn[i],a[i],1);
    		upd(dfo[i]+1,a[i],-1);
    	}
    	while(q--)
    	{
    		int k=read(),x=read(),y=read();
    		if(k==0)
    		{
    			upd(dfn[x],a[x],-1);
    			upd(dfo[x]+1,a[x],1);
    			a[x]=y;
    			upd(dfn[x],a[x],1);
    			upd(dfo[x]+1,a[x],-1);
    		}
    		else
    		{
    			int t=lca(x,y);v.clear();
    			ask(dfn[x],1);
    			ask(dfn[y],1);
    			ask(dfn[t],-1);
    			ask(dfn[fa[t][0]],-1);
    			t=0;
    			for(int i=0;i<v.size();i++)
    				t+=sum[v[i].first]*v[i].second;
    			if(t<k) puts("invalid request!");
    			else printf("%d
    ",qry(1,up,k)); 
    		}
    	}
    }
    
  • 相关阅读:
    Daily Scrumming 2015.10.20(Day 1)
    Buaaclubs项目介绍
    [转载] Linux创建用户后,切换用户报This account is currently not available
    NetFPGA-1G-CML从零开始环境配置
    Digilent Xilinx USB Jtag cable
    OVS流表table之间的跳转
    Linux换源
    Scapy安装以及简单使用
    Do in SDN
    KMP算法
  • 原文地址:https://www.cnblogs.com/C202044zxy/p/14548167.html
Copyright © 2011-2022 走看看