BZOJ 3772 精神污染 可持久化线段树

题目大意:给定一棵树和树上的m条路径,求这m条路径中任选两条不同的路径时其中一条包含另一条的概率是多少

这题还真是精神污染- -

首先一个显而易见的结论就是如果路径A包含于路径B 那么就有A的两端点在路径B上 这是个充要条件

于是我们对于每个A路径的两段点x和y,将x开一个vector,把y存进去

这样对于每个B路径 我们要找的就是B路径上的所有点在vector中有多少出边也在这条B路径上

有些拗口- -

那么我们可以维护树上主席树来确定B链对应的线段树

我们要确定的就是B链对应的线段树中有多少点在B链上

那么维护入栈出栈序就可以了

将每个点维护一个可持久化线段树 版本是父亲版本加上该节点的vector中所有元素在入栈出栈序上的位置

由于是入栈出栈序 因此入栈为1 出栈为-1

由于入栈出栈序只能查询一个节点指向根的一条链 因此我们将[x,y]这条链拆成[x,lca]和[lca,y]两段

这其中由于lca被算了两次 因此还要减掉[lca,lca] 就可以了

注意自己对自己的影响不计入答案

- -卡内存差评

#include <vector>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define M 100100
using namespace std;

struct Segtree{
	Segtree *ls,*rs;
	int val;
	void* operator new (size_t,Segtree *_,Segtree *__,int ___)
	{
		static Segtree mempool[3804000],*C=mempool;
		C->ls=_;
		C->rs=__;
		C->val=___;
		return C++;
	}
	Segtree* Insert(int x,int y,int pos,int val)
	{
		int mid=x+y>>1;
		if(x==y)
			return new (0x0,0x0,this->val+val) Segtree;
		if(pos<=mid)
			return new (ls->Insert(x,mid,pos,val),rs,this->val+val) Segtree;
		else
			return new (ls,rs->Insert(mid+1,y,pos,val),this->val+val) Segtree;
	}
	friend int Get_Ans(Segtree *p1,Segtree *p2,Segtree *p3,Segtree *p4,int x,int y,int l,int r)
	{
		int mid=x+y>>1;
		if(x==l&&y==r)
			return p1->val+p2->val-p3->val-p4->val;
		if(r<=mid)
			return Get_Ans(p1->ls,p2->ls,p3->ls,p4->ls,x,mid,l,r);
		if(l>mid)
			return Get_Ans(p1->rs,p2->rs,p3->rs,p4->rs,mid+1,y,l,r);
		return 	Get_Ans(p1->ls,p2->ls,p3->ls,p4->ls,x,mid,l,mid) +
				Get_Ans(p1->rs,p2->rs,p3->rs,p4->rs,mid+1,y,mid+1,r);
	}
}*tree[M];

struct Query{
	int x,y;
	bool operator < (const Query &q) const
	{
		if(x!=q.x)
			return x<q.x;
		return y<q.y;
	}
	bool operator == (const Query &q) const
	{
		return x==q.x && y==q.y;
	}
}queries[M];

struct abcd{
	int to,next;
}table[M<<1];
int head[M],tot;

int n,m;
int fa[M],dpt[M],ancestor[M][18];
int get_in[M],get_out[M];
vector<int> a[M];
long long A,B;

void Add(int x,int y)
{
	table[++tot].to=y;
	table[tot].next=head[x];
	head[x]=tot;
}
void DFS1(int x)
{
	static int cnt;
	int i;
	dpt[x]=dpt[fa[x]]+1;
	get_in[x]=++cnt;
	for(i=head[x];i;i=table[i].next)
		if(table[i].to!=fa[x])
		{
			fa[table[i].to]=x;
			ancestor[table[i].to][0]=x;
			DFS1(table[i].to);
		}
	get_out[x]=++cnt;
}
void DFS2(int x)
{
	int i;

	vector<int>::iterator it;
	tree[x]=tree[fa[x]];
	for(it=a[x].begin();it!=a[x].end();it++)
	{
		tree[x]=tree[x]->Insert(1,n<<1,get_in[*it],1);
		tree[x]=tree[x]->Insert(1,n<<1,get_out[*it],-1);
	}

	for(i=head[x];i;i=table[i].next)
		if(table[i].to!=fa[x])
			DFS2(table[i].to);
}
int LCA(int x,int y)
{
	int j;
	if(dpt[x]<dpt[y])
		swap(x,y);
	for(j=17;~j;j--)
		if(dpt[ancestor[x][j]]>=dpt[y])
			x=ancestor[x][j];
	if(x==y) return x;
	for(j=17;~j;j--)
		if(ancestor[x][j]!=ancestor[y][j])
			x=ancestor[x][j],y=ancestor[y][j];
	return fa[x];
}
int main()
{
	
	//freopen("3772.in","r",stdin);
	//freopen("3772.out","w",stdout);
	
	int i,j,x,y;
	cin>>n>>m;
	for(i=1;i<n;i++)
		scanf("%d%d",&x,&y),Add(x,y),Add(y,x);
	for(i=1;i<=m;i++)
	{
		scanf("%d%d",&x,&y);
		a[x].push_back(y);
		queries[i].x=x;
		queries[i].y=y;
	}
	tree[0]=new (0x0,0x0,0) Segtree;
	tree[0]->ls=tree[0]->rs=tree[0];
	DFS1(1);
	DFS2(1);
	for(j=1;j<=17;j++)
		for(i=1;i<=n;i++)
			ancestor[i][j]=ancestor[ancestor[i][j-1]][j-1];
	for(i=1;i<=m;i++)
	{
		x=queries[i].x;
		y=queries[i].y;
		int lca=LCA(x,y);
		A+=Get_Ans(tree[x],tree[y],tree[lca],tree[fa[lca]],1,n<<1,get_in[lca],get_in[x]);
		A+=Get_Ans(tree[x],tree[y],tree[lca],tree[fa[lca]],1,n<<1,get_in[lca],get_in[y]);
		A-=Get_Ans(tree[x],tree[y],tree[lca],tree[fa[lca]],1,n<<1,get_in[lca],get_in[lca]);
		A--;
	}
	sort(queries+1,queries+m+1);
	for(i=1;i<=m;i=j)
	{
		for(j=i+1;j<=m&&queries[i]==queries[j];j++);
		A-=(long long)(j-i)*(j-i-1)>>1;
	}
	B=(long long)m*(m-1)>>1;
	long long gcd=__gcd(A,B);
	A/=gcd;B/=gcd;
	cout<<A<<'/'<<B<<endl;
	return 0;
}


你可能感兴趣的:(bzoj,可持久化线段树,BZOJ3772)