[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

样例解释
可以选择的路径对有(1,2),(1,3),(2,3),只有路径1完全覆盖路径2。

HINT

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

题目分析

问题可以转换成:有多少条路径互相包含
那么一条路径的两个端点一定在另一条路径的两个端点的路径之间
那我们可以维护每个点的入栈出栈序 入栈1,出栈-1 ,将所有询问的y塞进x的vector
每到一个点 建立主席树 把x所有的y的入栈位置+1,出栈位置-1
然后枚举所有询问 根据lca进行差分查询
最后答案-m 因为自己包含自己不算

#include <cstdio>
#include <cstring>
#include <set>
#include <map>
#include <vector>
#include <cmath>
#include <queue>
#include <algorithm>
using namespace std;
int n,m,tot;
vector<int> s[100010];
vector<int>::iterator it;
int head[100010],to[200010],net[200010];
void add(int x,int y) { net[++tot]=head[x],head[x]=tot,to[tot]=y; }
int deep[100010],top[100010],size[100010],fa[100010],son[100010];
int in[100010],out[100010],cnt;
struct your
{
    int lson,rson,size;
}a[200010*20];
int root[100010],nm;
void update(int &x,int y,int dx,int val,int l,int r)
{
    x=++nm;
    if(l==r) { a[x].size=a[y].size+val; return ; }
    a[x].size=a[y].size+val;
    int mid=(l+r)>>1;
    if(dx<=mid) a[x].rson=a[y].rson,update(a[x].lson,a[y].lson,dx,val,l,mid);
    else a[x].lson=a[y].lson,update(a[x].rson,a[y].rson,dx,val,mid+1,r);
}
int ask(int x,int y,int z,int k,int l,int r,int dx,int dy)
{
    if(dx<=l&&r<=dy) 
        return a[x].size+a[y].size-a[z].size-a[k].size;
    int mid=(l+r)>>1;
    if(dy<=mid) return ask(a[x].lson,a[y].lson,a[z].lson,a[k].lson,l,mid,dx,dy);
    else if(dx>mid) return ask(a[x].rson,a[y].rson,a[z].rson,a[k].rson,mid+1,r,dx,dy);
    else return ask(a[x].lson,a[y].lson,a[z].lson,a[k].lson,l,mid,dx,mid)
               +ask(a[x].rson,a[y].rson,a[z].rson,a[k].rson,mid+1,r,mid+1,dy);   
}
void dfs(int x)
{
    in[x]=++cnt,deep[x]=deep[fa[x]]+1,size[x]=1;
    for(int i=head[x];i;i=net[i])
    {
        if(to[i]==fa[x]) continue;
        fa[to[i]]=x,dfs(to[i]),size[x]+=size[to[i]];
        if(size[son[x]]<size[to[i]]) son[x]=to[i];
    }
    out[x]=++cnt;
}
void dfs2(int x,int temp)
{
    top[x]=temp;
    if(son[x]) dfs2(son[x],temp);
    for(int i=head[x];i;i=net[i])
        if(to[i]!=fa[x]&&to[i]!=son[x]) dfs2(to[i],to[i]);
}
void dfs3(int x)
{
    root[x]=root[fa[x]];
    for(it=s[x].begin();it!=s[x].end();it++)
        update(root[x],root[x],in[*it],1,1,cnt),update(root[x],root[x],out[*it],-1,1,cnt);
    for(int i=head[x];i;i=net[i])
        if(to[i]!=fa[x]) dfs3(to[i]);
}
int lca(int x,int y)
{
    while(top[x]!=top[y])
    {
        if(deep[top[x]]<deep[top[y]]) swap(x,y);
        x=fa[top[x]]; 
    }
    return deep[x]<=deep[y]?x:y;
}
long long ans;
long long gcd(long long x,long long y)
{
    return !y?x:gcd(y,x%y);
}
int main()
{   
    scanf("%d%d",&n,&m);
    for(int x,y,i=1;i<n;i++) 
        scanf("%d%d",&x,&y),add(x,y),add(y,x);
    dfs(1),dfs2(1,1);
    for(int x,y,i=1;i<=m;i++) 
        scanf("%d%d",&x,&y),s[x].push_back(y);
    dfs3(1);        
    for(int i=1;i<=n;i++)
        for(it=s[i].begin();it!=s[i].end();it++)
        {
            int l=lca(*it,i);
            ans=ans+ask(root[i],root[*it],root[l],root[fa[l]],1,cnt,in[l],in[i]);
            ans=ans+ask(root[i],root[*it],root[l],root[fa[l]],1,cnt,in[l],in[*it]);
            ans=ans-ask(root[i],root[*it],root[l],root[fa[l]],1,cnt,in[l],in[l]);
            ans--;
        }
    long long tmp=(long long) m*(m-1)/2;
    long long g=gcd(ans,tmp);   
    printf("%lld/%lld",ans/g,tmp/g);
    return 0;   
}

发表评论

邮箱地址不会被公开。 必填项已用*标注