【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

题解
http://blog.csdn.net/popoqqq/article/details/43122821
https://www.cnblogs.com/fengzhiyuan/p/8082507.html(大佬博客Orz)

代码

#include<bits/stdc++.h>
#define ll long long
#define inf 1000000000
#define pa pair<ll,ll>
using namespace std;
const int mod=1000000007;
const int N=30005;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
ll gcd(ll a,ll b)
{
    return b==0?a:gcd(b,a%b);
}
ll A,B;
int ls[3900005],rs[3900005],sum[3900005];
int n,m,sz,tim,tot;
int Head[100005],dep[100005],rt[200005],l[100005],r[100005];
int fa[100005][17],ret[200005],Next[200005];
vector<int> a[100005];
struct node{int x,y;}b[100005];
inline void ins(int u,int v)
{
    ret[++tot]=v;Next[tot]=Head[u];Head[u]=tot;
}
void dfs1(int now)
{
    l[now]=++tim;
    for (int i=1;i<=16;i++) fa[now][i]=fa[fa[now][i-1]][i-1];
    for (int i=Head[now];i;i=Next[i])
    {
        if (ret[i]==fa[now][0]) continue;
        fa[ret[i]][0]=now;dep[ret[i]]=dep[now]+1;
        dfs1(ret[i]);
    }
    r[now]=++tim;
}
void insert(int &k1,int k2,int l,int r,int x,int y)
{
    k1=++sz;sum[k1]=sum[k2]+y;
    if (l==r) return;
    ls[k1]=ls[k2];rs[k1]=rs[k2];
    int mid=(l+r)>>1;
    if (x<=mid) insert(ls[k1],ls[k2],l,mid,x,y);
    else insert(rs[k1],rs[k2],mid+1,r,x,y);
}
void dfs2(int now)
{
    rt[now]=rt[fa[now][0]];
    for (int i=0;i<a[now].size();i++)
    {
        insert(rt[now],rt[now],1,tim,l[a[now][i]],1);
        insert(rt[now],rt[now],1,tim,r[a[now][i]],-1);
    }
    for (int i=Head[now];i;i=Next[i])
    {
        if (ret[i]==fa[now][0]) continue;
        dfs2(ret[i]);
    }
}
int lca(int a,int b)
{
    if (dep[a]<dep[b]) swap(a,b);
    int i;for (i=0;(1<<i)<=dep[a];i++);i--;
    for (int j=i;j>=0;j--)
        if (dep[a]-(1<<j)>=dep[b]) a=fa[a][j];
    if (a==b) return a;
    for (int j=i;j>=0;j--)
        if (fa[a][j]!=fa[b][j]) a=fa[a][j],b=fa[b][j];
    return fa[a][0];        
}
int query(int k1,int k2,int k3,int k4,int l,int r,int x,int y)
{
    if (l==x&&r==y) return sum[k1]+sum[k2]-sum[k3]-sum[k4];
    int mid=(l+r)>>1;
    if (y<=mid) return query(ls[k1],ls[k2],ls[k3],ls[k4],l,mid,x,y);
    else if (x>mid) return query(rs[k1],rs[k2],rs[k3],rs[k4],mid+1,r,x,y);
    else return query(ls[k1],ls[k2],ls[k3],ls[k4],l,mid,x,mid)+query(rs[k1],rs[k2],rs[k3],rs[k4],mid+1,r,mid+1,y);
}
void work()
{
    for (int i=1;i<=m;i++)
    {
        int x=b[i].x,y=b[i].y,f=lca(x,y);
        A+=query(rt[x],rt[y],rt[f],rt[fa[f][0]],1,tim,l[f],l[x]);
        A+=query(rt[x],rt[y],rt[f],rt[fa[f][0]],1,tim,l[f],l[y]);
        A-=query(rt[x],rt[y],rt[f],rt[fa[f][0]],1,tim,l[f],l[f]);
        A--;
    }
} 
int main()
{
    n=read();m=read();
    for (int i=1;i<n;i++)
    {
        int u=read(),v=read();
        ins(u,v);ins(v,u);
    }
    for (int i=1;i<=m;i++)
    {
        int x=read(),y=read();
        a[x].push_back(y);
        b[i].x=x;b[i].y=y;
    }
    dfs1(1);dfs2(1);work(); 
    B=(ll)m*(m-1)/2;
    ll d=gcd(A,B);
    A/=d;B/=d;
    printf("%lld/%lld",A,B);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值