BZOJ3772精神污染——可持久化线段树+出栈入栈序

题目描述

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

输入

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

输出

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

样例输入

5 3
1 2
2 3
3 4
2 5
3 5
2 5
1 4

样例输出

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

提示

100%的数据满足:N,M<=100000
 
  题目那么长,就最后一句有用QAQ。首先,一条路径A如果被另一条路径B覆盖,那么A路径的两端一定在B路径上。我们假设一条路径(x,y),x为起点,y为终点,对于每个点x存一下以它为起点的路径终点都有哪些,再维护整棵树的出栈入栈序,把出栈入栈序架在线段树上,然后再在原树上建主席树,每个点由父亲节点的主席树转移过来,在每个点对应的线段树上将以这个点为起点的所有终点在线段树上对应的出栈入栈位置+1或-1(入栈位置+1,出栈位置-1)。对于每条路径(x,y)的查询就是求两个点到它们lca的链所对应的主席树上按出栈入栈序区间求和。
#include<map>
#include<set>
#include<queue>
#include<cmath>
#include<stack>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define mid ((L+R)>>1)
using namespace std;
int x,y;
int n,m;
int cnt;
int num;
int tot;
int anc;
long long ans;
struct node
{
    int x;
    int y;
}q[100010];
int d[100010];
int in[100010];
int to[200010];
int l[4000010];
int r[4000010];
int out[100010];
int sum[4000010];
int next[200010];
int head[100010];
int root[100010];
int f[100010][18];
vector<int>v[100010];
bool cmp(node a,node b)
{
    if(a.x==b.x)
    {
        return a.y<b.y;  
    }
    return a.x<b.x;
}
void add(int x,int y)
{
    tot++;
    next[tot]=head[x];
    head[x]=tot;
    to[tot]=y;
}
long long gcd(long long x,long long y)
{
    if(y==0)
    {
        return x;
    }
    return gcd(y,x%y);
}
int updata(int pre,int L,int R,int x,int k)
{
    int rt=++cnt;
    if(L==R)
    {
        sum[rt]=sum[pre]+k;
        return rt;
    }
    l[rt]=l[pre];
    r[rt]=r[pre];
    if(x<=mid)
    {
        l[rt]=updata(l[pre],L,mid,x,k); 
    }   
    else
    {
        r[rt]=updata(r[pre],mid+1,R,x,k);
    }
    sum[rt]=sum[l[rt]]+sum[r[rt]];
    return rt;
}
int query(int x,int y,int fa,int anc,int L,int R,int ll,int rr)
{
    if(ll==L&&rr==R)
    {
        return sum[x]+sum[y]-sum[fa]-sum[anc];
    }
    if(ll>mid)
    {
        return query(r[x],r[y],r[fa],r[anc],mid+1,R,ll,rr);
    }
    else if(rr<=mid)
    {
        return query(l[x],l[y],l[fa],l[anc],L,mid,ll,rr);
    }
    else
    {
        return query(l[x],l[y],l[fa],l[anc],L,mid,ll,mid)+query(r[x],r[y],r[fa],r[anc],mid+1,R,mid+1,rr);
    }
}
void dfs1(int x,int fa)
{
    in[x]=++num;
    f[x][0]=fa;
    d[x]=d[fa]+1;
    for(int i=1;i<=17;i++)
    {
        f[x][i]=f[f[x][i-1]][i-1];
    }
    for(int i=head[x];i;i=next[i])
    {
        if(to[i]!=fa)
        {
            dfs1(to[i],x);
        }
    }
    out[x]=++num;
}
void dfs2(int x,int fa)
{
    root[x]=root[fa];
    int len=v[x].size();
    for(int i=0;i<len;i++)
    {
        root[x]=updata(root[x],1,num,in[v[x][i]],1);
        root[x]=updata(root[x],1,num,out[v[x][i]],-1);
    }
    for(int i=head[x];i;i=next[i])
    {
        if(to[i]!=fa)
        {
            dfs2(to[i],x);
        }
    }
}
int lca(int x,int y)
{
    if(d[x]<d[y])
    {
        swap(x,y);
    }
    int dep=d[x]-d[y];
    for(int i=0;i<=17;i++)
    {
        if(((1<<i)&dep))
        {
            x=f[x][i];
        }
    }
    if(x==y)
    {
         
        return x;
    }
    for(int i=17;i>=0;i--)
    {
        if(f[x][i]!=f[y][i])
        {
            x=f[x][i];
            y=f[y][i];
        }
    }
    return f[x][0];
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<n;i++)
    {
        scanf("%d%d",&x,&y);
        add(x,y);
        add(y,x);
    }
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&x,&y);
        v[x].push_back(y);
        q[i].x=x;
        q[i].y=y;
    }
    dfs1(1,0);
    dfs2(1,0);
    sort(q+1,q+m+1,cmp);
    for(int i=1;i<=m;i++)
    {
        x=q[i].x;
        y=q[i].y;
        anc=lca(x,y);
        ans+=query(root[x],root[y],root[anc],root[f[anc][0]],1,num,in[anc],in[x]);
        ans+=query(root[x],root[y],root[anc],root[f[anc][0]],1,num,in[anc],in[y]);
        ans-=query(root[x],root[y],root[anc],root[f[anc][0]],1,num,in[anc],in[anc]);
        ans--;
    }
    long long k=gcd(ans,1ll*m*(m-1)/2);
    long long g=1ll*m*(m-1)/2/k;
    ans/=k;
    printf("%lld/",ans);
    printf("%lld",g);
}
 

 

转载于:https://www.cnblogs.com/Khada-Jhin/p/9473384.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值