zoukankan      html  css  js  c++  java
  • bzoj4539: [Hnoi2016]树

    传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=4539

    思路:首先把大树缩点,一个点代表一次操作复制的子树

    两个点之间的边权值就是两个子树的根在大树中的距离,这个可以在原树中用倍增求出

    至于从大树标号转成原树标号,就相当于求子树内编号第k大的点的编号,用可持久化线段树即可。

    询问的话,就先把两个点移到对应复制操作的子树的根,计算距离,再在缩好点的大树里跳到lca,计算距离,再把lca多算的那段减掉即可。

    考场上想到了60分,突然就不会求区间第k大了,然后就放弃了这题...

    细节较多,一波大讨论即可。

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    typedef long long ll;
    const ll maxn=100010,maxm=200010,maxk=22,maxt=2200000;
    using namespace std;
    int n,m,Q,dfn[maxn],last[maxn],tim,pw[maxk];
    int root[maxn],from[maxn],idx;ll cnt[maxn],nn;
    //root 该点表示子树的根,from该点接在原树哪个点下面,cnt该次操作后新树的大小,nn新树大小
    void read(int &x){
    	char ch;
    	for (ch=getchar();!isdigit(ch);ch=getchar());
    	for (x=0;isdigit(ch);ch=getchar()) x=x*10+ch-'0';
    }
    void read(ll &x){
    	char ch;
    	for (ch=getchar();!isdigit(ch);ch=getchar());
    	for (x=0;isdigit(ch);ch=getchar()) x=x*10+ch-'0';
    }
    struct Tsegment{
    	#define ls ch[p][0]
    	#define rs ch[p][1]
    	#define mid ((l+r)>>1)
    	int siz[maxt],ch[maxt][2],root[maxn],tot;
    	void insert(int pre,int &p,int l,int r,int x){
    		p=++tot,siz[p]=siz[pre]+1;
    		if (l==r) return;
    		if (x<=mid) rs=ch[pre][1],insert(ch[pre][0],ls,l,mid,x);
    		else ls=ch[pre][0],insert(ch[pre][1],rs,mid+1,r,x);
    	}
    	void insert(int id,int v){insert(root[id-1],root[id],1,n,v);}
    	int query(int pre,int p,int l,int r,int k){
    		if (l==r){return l;}
    		if (siz[ls]-siz[ch[pre][0]]>=k) return query(ch[pre][0],ls,l,mid,k);
    		else return query(ch[pre][1],rs,mid+1,r,k-(siz[ls]-siz[ch[pre][0]]));
    	}
    	int query(int x,int y,int k){return query(root[x-1],root[y],1,n,k);}
    }T;
    
    struct Tgraph1{
    	int pre[maxm],now[maxn],son[maxm],tot,dep[maxn],fa[maxn][maxk],siz[maxn];
    	ll dis[maxn],val[maxm];
    	void add(int a,int b,ll c){pre[++tot]=now[a],now[a]=tot,son[tot]=b,val[tot]=c;}
    	void ins(int a,int b,ll c){add(a,b,c),add(b,a,c);}
    	void dfs(int x){
    		for (int i=1;i<=18;i++) fa[x][i]=fa[fa[x][i-1]][i-1];
    		for (int y=now[x];y;y=pre[y]) if (son[y]!=fa[x][0])
    			fa[son[y]][0]=x,dis[son[y]]=dis[x]+val[y],dep[son[y]]=dep[x]+1,dfs(son[y]);
    	}
    	void dfs2(int x){
    		siz[x]=1,dfn[x]=++tim,T.insert(tim,x);
    		for (ll y=now[x];y;y=pre[y]) if (son[y]!=fa[x][0]) dfs2(son[y]),siz[x]+=siz[son[y]];
    		last[x]=tim;
    	}
    	void jump(int &x,int h){for (int i=18;i>=0;i--) if (h&pw[i]) x=fa[x][i],h-=pw[i];}
    	int lca(int x,int y){
    		if (dep[x]<dep[y]) swap(x,y);
    		jump(x,dep[x]-dep[y]);
    		if (x==y) return x;
    		for (int i=18;i>=0;i--) if (fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
    		return fa[x][0];
    	}
    	ll dist(int x,int y){return dis[x]+dis[y]-2*dis[lca(x,y)];}
    	int up(int x,int y){jump(x,dep[x]-dep[y]-1);return x;}
    }ori,g;
    int getid(ll x){return lower_bound(cnt+1,cnt+1+idx,x)-cnt;}
    
    ll query(ll a,ll b){
    	int ida=getid(a),rta=root[ida],aa=T.query(dfn[rta],last[rta],a-cnt[ida-1]);
    	int idb=getid(b),rtb=root[idb],bb=T.query(dfn[rtb],last[rtb],b-cnt[idb-1]);
    	int lca=g.lca(ida,idb);
    	if (ida==idb) return ori.dist(aa,bb);
    	ll res=g.dist(ida,idb)+ori.dis[aa]-ori.dis[rta]+ori.dis[bb]-ori.dis[rtb];
    	if (ida==lca){
    		int frb=from[g.up(idb,lca)];
    		res-=ori.dis[aa]+ori.dis[frb]-ori.dist(aa,frb)-2*ori.dis[rta];
    	}
    	else if (idb==lca){
    		int fra=from[g.up(ida,lca)];
    		res-=ori.dis[bb]+ori.dis[fra]-ori.dist(bb,fra)-2*ori.dis[rtb];
    	}
    	else{
    		int fra=from[g.up(ida,lca)];
    		int frb=from[g.up(idb,lca)];
    		res-=ori.dis[fra]+ori.dis[frb]-ori.dist(fra,frb)-2*ori.dis[root[lca]];
    	}
    	return res;
    }
    
    int main(){
    	//freopen("tree.in","r",stdin);freopen("tree.out","w",stdout);
    	scanf("%d%d%d",&n,&m,&Q);
    	pw[0]=1;for (int i=1;i<=19;i++) pw[i]=pw[i-1]<<1;
    	for (int i=1,x,y;i<n;i++) read(x),read(y),ori.ins(x,y,1);
    	ori.dfs(1),ori.dfs2(1);
    	nn=n,cnt[1]=nn,root[1]=idx=1;
    	for (int i=2;i<=m+1;i++){
    		ll x,y;
    		read(x),read(y);
    		int id=getid(y),rt=root[id];
    		root[i]=x,idx=i,from[i]=T.query(dfn[rt],last[rt],y-cnt[id-1]);
    		g.ins(i,id,ori.dis[from[i]]-ori.dis[rt]+1);
    		nn+=ori.siz[x],cnt[i]=nn;
    	}
    	g.dfs(1);
    	for (int i=1;i<=Q;i++){
    		ll a,b;read(a),read(b);
    		printf("%lld
    ",query(a,b));
    	}
    	return 0;
    }


  • 相关阅读:
    linux入门学习
    qt 常见问题记录
    我今年,二十七八岁了
    C#程序如何实现设置系统WIFI共享
    主界面设计(收集)
    Qt中如何添加ICON图标
    系统登录界面(收集)
    Qt类型转换
    qt 系统设计对比
    Qt中丰富的容器类数组QVector、链表QLinkedList、映射表QMap、哈希表QHash
  • 原文地址:https://www.cnblogs.com/thythy/p/5493639.html
Copyright © 2011-2022 走看看