zoukankan      html  css  js  c++  java
  • BZOJ 3772 精神污染 主席树+欧拉序

    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。

     附:这道题真的是精神污染

    机房的人RE和MLE记录:

    我们可以通过两种方式计算答案:

    1.算出每条路径覆盖几条路径

    2.计算每条路径被几条路径覆盖

    那么这道题是从第一个角度考虑,对于一条路径(u,v)任意选取一个端点,假设把u作为端点,把v塞入u的动态数组中,然后对整颗树进行dfs,求出欧拉序,再对整棵树进行第二遍dfs,把动态数组中对应的节点在L处加1,R处减1,那么在统计答案的时候对选定端点的限制体现在主席树选用的根为root[u],root[v],root[lca(u,v)],root[f[lca(u,v)][0]],限制了选定端点,然后进行区间查询,查询[L[lca],L[u]]和[L[lca],L[v]],因为L[lca]被算了两遍,所以要再次减去L[lca]的贡献,而且不能算上自己在L[u]或L[v]的贡献,然后Ans需要减1,然后就得到答案了。炸int注意。。。

    code:

    #include <stdio.h>
    #include <cstring>
    #include <vector>
    const int MAXN = 100005;
    typedef long long ll;
    int first[MAXN],e=1,deep[MAXN],f[MAXN][20],size,cnt,root[MAXN],L[MAXN],R[MAXN],n,m;
    std :: vector<int>G[MAXN];
     
     
    template<typename _t>
    inline _t read(){
        _t x=0,f=1;
        char ch=getchar();
        for(;ch>'9'||ch<'0';ch=getchar())if(ch=='-')f=-f;
        for(;ch>='0'&&ch<='9';ch=getchar())x=x*10+(ch^48);
        return x*f;
    }
    ll gcd(ll x,ll y){return y==0?x:gcd(y,x%y);}
     
    struct node{
        int l,r,sum;
    }tree[MAXN*39];
     
    void insert(int &o,int old,int l,int r,int pos,int val){
        o=++cnt;
        tree[o]=tree[old];
        tree[o].sum+=val;
        if(l==r)return;
        int m=l+r>>1;
        if(pos<=m)insert(tree[o].l,tree[old].l,l,m,pos,val);
        else insert(tree[o].r,tree[old].r,m+1,r,pos,val);
    }
     
    #define l(x) tree[x].l
    #define r(x) tree[x].r
    #define s(x) tree[x].sum
     
    inline ll Query(int t1,int t2,int t3,int t4,int x,int y,int l,int r){
        if(x<=l&&r<=y)return s(t1)+s(t2)-s(t3)-s(t4);
        ll Ans = 0;int m=l+r>>1; 
        if(x<=m)Ans+=Query(l(t1),l(t2),l(t3),l(t4),x,y,l,m);
        if(m<y)Ans+=Query(r(t1),r(t2),r(t3),r(t4),x,y,m+1,r);
        return Ans;
    }
     
    struct edge{
        int v,next;
    }a[MAXN<<1];
     
    struct Query{
        int x,y;
    }q[MAXN];
     
    inline void push(int u,int v){
        a[e].v=v;a[e].next=first[u];
        first[u]=e++;
    }
     
    void dfs(int u,int fa){
        L[u]=++size;
        f[u][0]=fa;deep[u]=deep[fa]+1;
        for(int i=1;i<=17;i++)
            f[u][i]=f[f[u][i-1]][i-1];
        for(int i=first[u];i;i=a[i].next)
            if(a[i].v!=fa)dfs(a[i].v,u);
        R[u]=++size;
        return;
    }
     
    void __dfs(int u){
        root[u]=root[f[u][0]];
        for(int i=0;i<G[u].size();i++)
            insert(root[u],root[u],1,size,L[G[u][i]],1),
            insert(root[u],root[u],1,size,R[G[u][i]],-1);
        for(int i=first[u];i;i=a[i].next)
            if(a[i].v!=f[u][0])
                __dfs(a[i].v);
    }
     
    inline int lca(int u,int v){
        if(deep[u]<deep[v]){u^=v;v^=u;u^=v;}
        int t=deep[u]-deep[v];
        for(int i=0;i<=17;i++)
            if(t&(1<<i))
                u=f[u][i];
        if(u==v)return u;
        for(int i=17;i>=0;i--)
            if(f[u][i]!=f[v][i])
                u=f[u][i],v=f[v][i];
        return f[u][0];
    }
     
    int main(){ 
        n=read<int>();m=read<int>();
        for(int i=1;i<n;i++){
            int u=read<int>(),v=read<int>();
            push(u,v);push(v,u);
        }
        dfs(1,0);
        for(int i=1;i<=m;i++)q[i].x=read<int>(),q[i].y=read<int>(),G[q[i].x].push_back(q[i].y);
        __dfs(1);
     
        ll Ans = 0;
        for(int i=1;i<=m;i++){
            int Lca=lca(q[i].x,q[i].y);
            int t1=root[q[i].x],t2=root[q[i].y],t3=root[Lca],t4=root[f[Lca][0]];
            Ans+=Query(t1,t2,t3,t4,L[Lca],L[q[i].x],1,size);
            Ans+=Query(t1,t2,t3,t4,L[Lca],L[q[i].y],1,size);
            Ans-=Query(t1,t2,t3,t4,L[Lca],L[Lca],1,size);
            Ans--;
        }
        if(Ans==0){
            printf("0/1
    ");
            return 0;
        }
        ll tmp = gcd(Ans,(ll)m*(m-1)/2);
        printf("%lld/%lld
    ",Ans/tmp,(ll)m*(m-1)/2/tmp);
    }




  • 相关阅读:
    [分享] Knockout.js 体验之旅
    [分享] 用NodeJs做一个小爬虫,附源码!
    [分享] 纯CSS完美实现垂直水平居中的6种方式
    [分享] 来,做一个问卷调查(有抽奖!)
    除非你是BAT,前端开发中最好少造轮子
    [分享] 微信支付中商户对个人用户付款案例
    30分钟做一个二维码名片应用,有源码!
    【分享】WeX5的正确打开方式(7)——数据组件详解
    基于phonegap,html5,ratchet,handlebars等技术的微表情APP
    html5实现web app摇一摇换歌
  • 原文地址:https://www.cnblogs.com/Cooook/p/7738494.html
Copyright © 2011-2022 走看看