zoukankan      html  css  js  c++  java
  • [HEOI2016/TJOI2016]树

    洛咕

    题意:解决这样一个问题:给定一颗有根树,根为 (1) ,有以下两种操作:

    1. 标记操作:对某个结点打上标记.(在最开始,只有结点 (1) 有标记,其他结点均无标记,而且对于某个结点,可以打多次标记.)

    2. 询问操作:询问某个结点最近的一个打了标记的祖先.(这个结点本身也算自己的祖先).(n,m<=100000.)

    分析:每个节点的权值就是其最近的一个打了标记的祖先的深度,显然,初始每个节点的权值都为1,然后树链剖分后线段树维护区间最大值.

    对于每一次修改操作,如果这个节点之前没被标记,就单点修改权值.

    对于一次询问操作,直接查询(x)到根节点路径上的权值最大值即可.

    然后我就愉快地过了样例,交一发,发现只能过最后一个点.然后搞了半个小时才发现我这样输出的是该节点最近的一个打了标记的祖先的深度,所以只要根据这个深度再树上倍增一下就能求出编号了.(最后一个点为什么能过??显然,是因为这棵树是一条链,深度=编号).

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    #include<queue>
    #include<map>
    #include<set>
    #define ll long long
    using namespace std;
    inline int read(){
        int x=0,o=1;char ch=getchar();
        while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
        if(ch=='-')o=-1,ch=getchar();
        while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
        return x*o;
    }
    const int N=100005;
    int tot,head[N],nxt[N<<1],to[N<<1];
    inline void add(int a,int b){nxt[++tot]=head[a];head[a]=tot;to[tot]=b;}
    int size[N],dep[N],fa[N],son[N],seg[N],rev[N<<2],top[N];
    int ans,maxn[N<<2],bj[N],f[N][25];
    inline void dfs1(int u,int father){
    	size[u]=1;
    	for(int j=1;j<=20;++j)f[u][j]=f[f[u][j-1]][j-1];
    	for(int i=head[u];i;i=nxt[i]){
    		int v=to[i];if(v==father)continue;
    		dep[v]=dep[u]+1;fa[v]=u;f[v][0]=u;
    		dfs1(v,u);size[u]+=size[v];
    		if(size[v]>size[son[u]])son[u]=v;
    	}
    }
    inline void dfs2(int u,int father){
    	if(son[u]){
    		seg[son[u]]=++seg[0];
    		rev[seg[0]]=son[u];
    		top[son[u]]=top[u];
    		dfs2(son[u],u);
    	}
    	for(int i=head[u];i;i=nxt[i]){
    		int v=to[i];
    		if(!top[v]){
    			seg[v]=++seg[0];
    			rev[seg[0]]=v;
    			top[v]=v;dfs2(v,u);
    		}
    	}
    }
    inline void build(int p,int l,int r){
    	if(l==r){maxn[p]=1;return;}
    	int mid=(l+r)>>1;build(p<<1,l,mid);build(p<<1|1,mid+1,r);
    	maxn[p]=max(maxn[p<<1],maxn[p<<1|1]);
    }
    inline void change(int p,int l,int r,int pos,int val){
    	if(l==r&&pos==l){maxn[p]=max(maxn[p],val);return;}
    	int mid=(l+r)>>1;
    	if(pos<=mid)change(p<<1,l,mid,pos,val);
    	else change(p<<1|1,mid+1,r,pos,val);
    	maxn[p]=max(maxn[p<<1],maxn[p<<1|1]);
    }
    inline void query(int p,int l,int r,int ql,int qr){
    	if(ql<=l&&qr>=r){ans=max(ans,maxn[p]);return;}
    	int mid=(l+r)>>1;
    	if(ql<=mid)query(p<<1,l,mid,ql,qr);
    	if(qr>mid)query(p<<1|1,mid+1,r,ql,qr);
    }
    inline void ask(int x,int y){
    	while(top[x]!=top[y]){
    		if(dep[top[x]]<dep[top[y]])swap(x,y);
    		query(1,1,seg[0],seg[top[x]],seg[x]);
    		x=fa[top[x]];
    	}
    	if(dep[x]>dep[y])swap(x,y);
    	query(1,1,seg[0],seg[x],seg[y]);
    }
    int main(){
    	int n=read(),Q=read();
    	for(int i=1;i<n;++i){
    		int a=read(),b=read();
    		add(a,b);add(b,a);
    	}
    	seg[0]=seg[1]=dep[1]=rev[1]=top[1]=1;
    	dfs1(1,0);dfs2(1,0);build(1,1,seg[0]);
    	while(Q--){
    		char ch;cin>>ch;int x=read();
    		if(ch=='C'){
    			if(!bj[x])change(1,1,seg[0],seg[x],dep[x]);
    			bj[x]=1;
    		}
    		if(ch=='Q'){
    			if(bj[x]){printf("%d
    ",x);continue;}
    			ans=1;ask(x,1);int k=dep[x]-ans;//要向上跳k级
    			for(int j=0;j<=20;++j)
    				if(k&(1<<j))x=f[x][j];
    			printf("%d
    ",x);
    		}
    	}
        return 0;
    }
    
    
  • 相关阅读:
    Kerberos-KDC
    samba后台进程及安全模式简介
    samba服务器详细配置(非域模式)
    windows常用net use命令
    samba常用命令
    ORA-24324、ORA-12560、ORA-12514
    oracle的启动和关闭
    Oracle 监听配置详解(转载)
    linux加入windows域之完美方案(转载)
    怎样识吉他谱
  • 原文地址:https://www.cnblogs.com/PPXppx/p/11727204.html
Copyright © 2011-2022 走看看