Hihocoder #1167 : 高等理论计算机科学

题目大意

%   给定一棵 n n n 个点的树,给定树上的 P P P 条链,求有多少个无序数对 ( u , v ) (u,v) (u,v) 满足第 u u u 条链和第 v v v 条链有交点。
  数据范围  1 ⩽ n , P ⩽ 1 0 6 1\leqslant n,P\leqslant 10^6 1n,P106

题解

%   我们考虑每次加入一条链子,计算有多少条链和这条链有交点。在插入一条链的时候,给链上的点权加一 ,链上的边权减一,询问的时候就等价于一个链上求和(包括边上的和点上的)。

代码

#include<bits/stdc++.h>
using namespace std;
//两种合并后的线段树
struct node{
	int l,r,d,lazy;
	bool nodes;
	node *left,*right;
	node(int tl,int tr,bool tnodes):left(NULL),right(NULL),l(tl),r(tr),d(0),lazy(0),nodes(tnodes){
		if(tnodes) if(tl==tr) return;
		if(!tnodes) if(tl+1==tr) return;
		int mid=(tl+tr)>>1;
		left=new node(tl,mid,nodes);
		right=new node(mid+tnodes,tr,tnodes);
	}
	void update(){
		if(lazy&&left){
			left->lazy+=lazy;
			right->lazy+=lazy;
			left->d+=(left->r-left->l+nodes)*lazy;
			right->d+=(right->r-right->l+nodes)*lazy;
		}lazy=0;
	}
	void add(int tl,int tr,int td){
		if(l==tl&&r==tr){
			lazy+=td;
			d+=(r-l+nodes)*td;
			return;
		} update();
		if(tr<=left->r) left->add(tl,tr,td);
		else if(tl>=right->l) right->add(tl,tr,td);
		else left->add(tl,left->r,td),right->add(right->l,tr,td);
		d=left->d+right->d;
	}
	int sum(int tl,int tr){
		if(l==tl&&r==tr) return d;
		update();
		if(tr<=left->r) return left->sum(tl,tr);
		if(tl>=right->l) return right->sum(tl,tr);
		return left->sum(tl,left->r)+right->sum(right->l,tr);
	}
};

#define maxn 1000010
struct edge{
	int v,next;
}edges[maxn];
int n,head[maxn];
void ins(int u,int v){
	static int cnt=0;
	edges[++cnt]=(edge){v,head[u]};
	head[u]=cnt;
}
int fa[maxn],dep[maxn],size[maxn],son[maxn];
void predfs(int u){
	size[u]=1;
	for(int i=head[u];i;i=edges[i].next){
		int v=edges[i].v;
		if(v==fa[u]) continue;
		fa[v]=u;dep[v]=dep[u]+1;
		predfs(v); size[u]+=size[v];
		if(size[son[u]]<size[v]) son[u]=v;
	}
}
int top[maxn],code[maxn],init[maxn];
void recode(int u,int chain){
	static int cnt=0;
	top[init[code[u]=++cnt]=u]=chain;
	if(son[u]) recode(son[u],chain);
	for(int i=head[u];i;i=edges[i].next){
		int v=edges[i].v;
		if(v==fa[u]||v==son[u]) continue;
		recode(v,v);
	}
}
node *E,*V;
void add_node(int x,int y,int d){
	int tx=top[x],ty=top[y];
	while(tx!=ty){
		if(dep[tx]<dep[ty]) swap(x,y),swap(tx,ty);
		V->add(code[tx],code[x],d);
		x=fa[tx],tx=top[x];
	} if(code[x]>code[y]) swap(x,y);
	V->add(code[x],code[y],d);
}
int sum_node(int x,int y){
	int tx=top[x],ty=top[y];
	int ans=0;
	while(tx!=ty){
		if(dep[tx]<dep[ty]) swap(x,y),swap(tx,ty);
		ans+=V->sum(code[tx],code[x]);
		x=fa[tx],tx=top[x];
	} if(code[x]>code[y]) swap(x,y);
	return ans+V->sum(code[x],code[y]);
}

int W[maxn];
void add_edge(int x,int y,int d){
	int tx=top[x],ty=top[y];
	while(tx!=ty){
		if(dep[tx]<dep[ty]) swap(x,y),swap(tx,ty);
		if(code[tx]!=code[x]) E->add(code[tx],code[x],d);
		W[tx]+=d;
		x=fa[tx],tx=top[x];
	} if(code[x]>code[y]) swap(x,y);
	if(code[x]!=code[y]) E->add(code[x],code[y],d);
}
int sum_edge(int x,int y){
	int tx=top[x],ty=top[y];
	int ans=0;
	while(tx!=ty){
		if(dep[tx]<dep[ty]) swap(x,y),swap(tx,ty);
		if(code[tx]!=code[x]) ans+=E->sum(code[tx],code[x]);
		ans+=W[tx];
		x=fa[tx],tx=top[x];
	} if(code[x]>code[y]) swap(x,y);
	if(code[x]!=code[y]) ans+=E->sum(code[x],code[y]);
	return ans;
}

int add_link(int u,int v){
	add_node(u,v,1);
	add_edge(u,v,-1);
}

int sum_link(int u,int v){
	return sum_node(u,v)+sum_edge(u,v);
}

int main(){
	int m; scanf("%d%d",&n,&m);
	for(int i=1,a,b;i<n;i++){
		scanf("%d%d",&a,&b);
		ins(a,b);ins(b,a);
	} predfs(2);recode(2,2);
	E=new node(1,n,false);
	V=new node(1,n,true);
	long long cnt=0;
	for(int i=1,a,b;i<=m;i++){
		scanf("%d%d",&a,&b);
		cnt+=sum_link(a,b);
		add_link(a,b);
	} printf("%lld\n",cnt);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值