zoukankan      html  css  js  c++  java
  • [BZOJ3772]精神污染

    Description
    兵库县位于日本列岛的中央位置,北临日本海,南面濑户内海直通太平洋,中央部位是森林和山地,与拥有关西机场的大阪府比邻而居,是关西地区面积最大的县,是集经济和文化于一体的一大地区,是日本西部门户,海陆空交通设施发达。濑户内海沿岸气候温暖,多晴天,有日本少见的贸易良港神户港所在的神户市和曾是豪族城邑“城下町”的姬路市等大城市,还有以疗养地而闻名的六甲山地等。
    兵库县官方也大力发展旅游,为了方便,他们在县内的N个旅游景点上建立了n-1条观光道,构成了一棵图论中的树。同时他们推出了M条观光线路,每条线路由两个节点x和y指定,经过的旅游景点就是树上x到y的唯一路径上的点。保证一条路径只出现一次。
    你和你的朋友打算前往兵库县旅游,但旅行社还没有告知你们最终选择的观光线路是哪一条(假设是线路A)。这时候你得到了一个消息:在兵库北有一群丧心病狂的香菜蜜,他们已经选定了一条观光线路(假设是线路B),对这条路线上的所有景点都释放了【精神污染】。这个计划还有可能影响其他的线路,比如有四个景点1-2-3-4,而【精神污染】的路径是1-4,那么1-3,2-4,1-2等路径也被视为被完全污染了。
    现在你想知道的是,假设随便选择两条不同的路径A和B,存在一条路径使得如果这条路径被污染,另一条路径也被污染的概率。换句话说,一条路径被另一条路径包含的概率。

    Input
    第一行两个整数N,M
    接下来N-1行,每行两个数a,b,表示A和B之间有一条观光道。
    接下来M行,每行两个数x,y,表示一条旅游线路。

    Output
    所求的概率,以最简分数形式输出。

    Sample Input
    5 3
    1 2
    2 3
    3 4
    2 5
    3 5
    2 5
    1 4

    Sample Output
    1/3

    HINT
    100%的数据满足:N,M<=100000


    题目里一堆废话。。。除了最后一句。。。其实这题叫做“精神错乱”,写的我真的精神错乱了。。。

    废话不多说,我们进入正题

    首先考虑一条路径(u,v),其存在两种情况
    1.lca是其中的某个点
    2.lca不是其中的某个点

    对于第一种情况,我们令lca=u,那么能够覆盖其的路径的左右端点(l,r),在dfs序上必定满足

    (egin{cases}1&leqslant l'&<x'\v'&leqslant r'&<v'+size[v]end{cases})(egin{cases}x'+size[x]&<l'&leqslant n\v'&leqslant r'&<v'+size[v]end{cases})

    其中,(l',r',x')表示l,r,x的dfs序,size表示其子树大小,x表示v到u的路径上,u的儿子节点

    对于第二种情况,令u的dfs序小于v的dfs序,能覆盖其的路径的左右端点(l,r),在dfs序上也必定满足

    [egin{cases}u'leqslant l'<u'+size[u]\v'leqslant r'<v'+size[v]end{cases} ]

    因此我们可以按照dfs序的顺序建立主席树,每棵主席树上记录与它直接相连的点的dfs序,然后就可以直接区间查询了

    (ps:MD这题卡空间。。。)

    /*program from Wolfycz*/
    #include<cmath>
    #include<cstdio>
    #include<vector>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #define Fi first
    #define Se second
    #define inf 0x7f7f7f7f
    using namespace std;
    typedef long long ll;
    typedef unsigned int ui;
    typedef unsigned long long ull;
    inline char gc(){
    	static char buf[1000000],*p1=buf,*p2=buf;
    	return p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++;
    }
    inline int frd(){
    	int x=0,f=1;char ch=gc();
    	for (;ch<'0'||ch>'9';ch=gc())	if (ch=='-')    f=-1;
    	for (;ch>='0'&&ch<='9';ch=gc())	x=(x<<1)+(x<<3)+ch-'0';
    	return x*f;
    }
    inline int read(){
    	int x=0,f=1;char ch=getchar();
    	for (;ch<'0'||ch>'9';ch=getchar())	if (ch=='-')	f=-1;
    	for (;ch>='0'&&ch<='9';ch=getchar())	x=(x<<1)+(x<<3)+ch-'0';
    	return x*f;
    }
    inline void print(int x){
    	if (x<0)    putchar('-'),x=-x;
    	if (x>9)	print(x/10);
    	putchar(x%10+'0');
    }
    const int N=1e5,M=4e6;
    int dfn[N+10],root[N+10],n,m;
    vector<int>vec[N+10];
    struct S1{
    	int pre[(N<<1)+10],now[N+10],child[(N<<1)+10],tot,Time;
    	int fa[N+10],top[N+10],Rem[N+10],size[N+10],deep[N+10];
    	void join(int x,int y){pre[++tot]=now[x],now[x]=tot,child[tot]=y;}
    	void insert(int x,int y){join(x,y),join(y,x);}
    	void dfs(int x){
    		deep[x]=deep[fa[x]]+1,size[x]=1;
    		for (int p=now[x],son=child[p];p;p=pre[p],son=child[p]){
    			if (son==fa[x])	continue;
    			fa[son]=x,dfs(son);
    			size[x]+=size[son];
    			if (size[Rem[x]]<size[son])	Rem[x]=son;
    		}
    	}
    	void build(int x){
    		if (!x)	return;
    		top[x]=Rem[fa[x]]==x?top[fa[x]]:x;
    		dfn[x]=++Time;
    		build(Rem[x]);
    		for (int p=now[x],son=child[p];p;p=pre[p],son=child[p]){
    			if (son==fa[x]||son==Rem[x])	continue;
    			build(son);
    		}
    	}
    	pair<int,int> Lca(int x,int y){
    		int last=0;
    		while (top[x]!=top[y]){
    			if (deep[top[x]]<deep[top[y]])	swap(x,y);
    			last=top[x],x=fa[top[x]];
    		}
    		if (x==y)	return make_pair(x,last);
    		if (deep[x]>deep[y])	swap(x,y);
    		return make_pair(x,Rem[x]);
    	}
    }HLD;//Heavy Light Decomposition
    struct S2{
    	int ls[M+10],rs[M+10],cnt[M+10],tot;
    	void insert(int &k,int p,int l,int r,int x){
    		cnt[k=++tot]=cnt[p]+1;
    		ls[k]=ls[p],rs[k]=rs[p];
    		if (l==r)	return;
    		int mid=(l+r)>>1;
    		if (x<=mid)	insert(ls[k],ls[p],l,mid,x);
    		else	insert(rs[k],rs[p],mid+1,r,x);
    	}
    	int Query(int k,int p,int l,int r,int x){
    		if (l==r)	return cnt[p]-cnt[k];
    		int mid=(l+r)>>1,res=cnt[ls[p]]-cnt[ls[k]];
    		if (x<=mid)	return Query(ls[k],ls[p],l,mid,x);
    		else	return res+Query(rs[k],rs[p],mid+1,r,x);
    	}
    	int Query(int rl,int rr,int l,int r){return Query(root[rl-1],root[rr],1,n,r)-Query(root[rl-1],root[rr],1,n,l-1);}
    }CT;//Chairman Tree
    struct S3{
    	int x,y;
    	void insert(int _x,int _y){x=_x,y=_y;}
    }Ask[N+10];
    ll gcd(ll a,ll b){return !b?a:gcd(b,a%b);}
    signed main(){
    	n=read(),m=read();
    	for (int i=1;i<n;i++){
    		int x=read(),y=read();
    		HLD.insert(x,y);
    	}
    	HLD.dfs(1),HLD.build(1);
    	for (int i=1;i<=m;i++){
    		int x=read(),y=read();
    		vec[dfn[x]].push_back(dfn[y]);
    		vec[dfn[y]].push_back(dfn[x]);
    		Ask[i].insert(x,y);
    	}
    	for (int i=1;i<=n;i++){
    		root[i]=root[i-1];
    		for (vector<int>::iterator it=vec[i].begin();it!=vec[i].end();it++)	CT.insert(root[i],root[i],1,n,*it);
    	}
    	ll All=1ll*m*(m-1)/2,Ans=0;
    	for (int i=1;i<=m;i++){
    		int x=Ask[i].x,y=Ask[i].y;
    		pair<int,int>tmp=HLD.Lca(x,y);
    		if (dfn[x]>dfn[y])	swap(x,y);
    		if (tmp.Fi==x){
    			Ans+=CT.Query(1,dfn[tmp.Se]-1,dfn[y],dfn[y]+HLD.size[y]-1);
    			Ans+=CT.Query(dfn[tmp.Se]+HLD.size[tmp.Se],n,dfn[y],dfn[y]+HLD.size[y]-1);
    		}else	Ans+=CT.Query(dfn[x],dfn[x]+HLD.size[x]-1,dfn[y],dfn[y]+HLD.size[y]-1);
    	}
    	Ans-=m; ll GCD=gcd(All,Ans);
    	printf("%lld/%lld
    ",Ans/GCD,All/GCD);
    	return 0;
    }
    
  • 相关阅读:
    Android轩辕剑之ActionBar之三
    Android轩辕剑之ActionBar之二
    Android轩辕剑之ActionBar之一
    使用Android OpenGL ES 2.0绘图之六:响应触摸事件
    使用Android OpenGL ES 2.0绘图之五:添加运动
    使用Android OpenGL ES 2.0绘图之四:应用投影和相机视口
    使用Android OpenGL ES 2.0绘图之三:绘制形状
    使用Android OpenGL ES 2.0绘图之二:定义形状
    随笔编号-16 MySQL查看表及索引大小方法
    随笔编号-14 数据库连接最大数问题
  • 原文地址:https://www.cnblogs.com/Wolfycz/p/10000096.html
Copyright © 2011-2022 走看看