• BZOJ_3772_精神污染_主席树


    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。

    HINT

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

    写完之后发现有更简单的方法。。。
    方法一:离线树状数组
    首先有两种路径,1字形和V字形。
    对于1字型的路径,包含它的路径两端一定在链顶的儿子的子树外和链底的子树内,在dfs序上就划出了两个线段。
    对于V字形的路径,被它包含的路径两端一定在两个端点的子树内,这也是两个线段。
    把每条路经的端点在dfs序中的位置看成二维平面上的点,然后离线树状数组二维数点即可。
    方法二:出栈入栈序+主席树
    把询问挂链(x,y),然后每个点维护根到这个点路径的信息。
    在x这颗线段树上把y的入栈位置+1,出栈位置-1。
    然后把询问分成x->lca,lca->y,每次分别查询x到y路径上dfn[lca]到dfn[x],dfn[lca]到dfn[y]之间的和,其中dfn[x]表示x在dfs序中的位置。
    分析一下,对于其中包含的路径(z,w),z这个点一定在x到y路径上,故z刚刚做出的修改会在x到y这段路经上体现。
    那么我们就只需要找w,由于我把入栈位置+1出栈位置-1,相当于差分实现区间加,因此查询时会查到w。
     
    代码(方法二):
    #include <cstdio>
    #include <string.h>
    #include <algorithm>
    using namespace std;
    #define N 100050
    typedef long long ll;
    int head[N],to[N<<1],nxt[N<<1],cnt,n,m,root[N],maxn;
    int fa[N],top[N],siz[N],son[N],dep[N],dfn[N],S[N],out[N],t[N*39],tot,ls[N*39],rs[N*39];
    inline void add(int u,int v) {
        to[++cnt]=v; nxt[cnt]=head[u]; head[u]=cnt;
    }
    void insert(int &y,int x,int l,int r,int v,int c) {
        y=++tot; t[y]=t[x]+c;
        if(l==r) return ;
        int mid=(l+r)>>1;
        if(v<=mid) rs[y]=rs[x],insert(ls[y],ls[x],l,mid,v,c);
        else ls[y]=ls[x],insert(rs[y],rs[x],mid+1,r,v,c);
    }
    int query(int a,int b,int c,int d,int l,int r,int x,int y) {
        if(x<=l&&y>=r) return t[a]+t[b]-t[c]-t[d];
        int mid=(l+r)>>1,re=0;
        if(x<=mid) re+=query(ls[a],ls[b],ls[c],ls[d],l,mid,x,y);
        if(y>mid) re+=query(rs[a],rs[b],rs[c],rs[d],mid+1,r,x,y);
        return re;
    }
    void dfs1(int x,int y) {
        S[++S[0]]=x; dfn[x]=++maxn;
        int i; dep[x]=dep[y]+1; fa[x]=y; siz[x]=1;
        for(i=head[x];i;i=nxt[i]) if(to[i]!=y) {
            dfs1(to[i],x); siz[x]+=siz[to[i]];
            if(siz[to[i]]>siz[son[x]]) son[x]=to[i];
        }
        out[x]=++maxn;
    }
    void dfs2(int x,int t) {
        top[x]=t; int i; if(son[x]) dfs2(son[x],t);
        for(i=head[x];i;i=nxt[i]) if(to[i]!=fa[x]&&to[i]!=son[x]) dfs2(to[i],to[i]);
    }
    int lca(int x,int y) {
        while(top[x]!=top[y]) {
            if(dep[top[x]]>dep[top[y]]) swap(x,y);
            y=fa[top[y]];
        }
        return dep[x]<dep[y]?x:y;
    }
    ll gcd(ll x,ll y) {
        return y?gcd(y,x%y):x;
    }
    int main() {
        scanf("%d%d",&n,&m);
        int i,x,y,j;
        for(i=1;i<n;i++) {
            scanf("%d%d",&x,&y);
            add(x,y); add(y,x);
        }
        dfs1(1,0);
        dfs2(1,1);
        memset(head,0,sizeof(head)); cnt=0;
        for(i=1;i<=m;i++) {
            scanf("%d%d",&x,&y);
            add(x,y);
        }
        for(j=1;j<=n;j++) {
            x=S[j];
            root[x]=root[fa[x]];
            for(i=head[x];i;i=nxt[i]) {
                y=to[i];
                insert(root[x],root[x],1,maxn,dfn[y],1);
                insert(root[x],root[x],1,maxn,out[y],-1);
            }
        }
        ll ans=0,dev=1ll*m*(m-1)/2;;
        for(x=1;x<=n;x++) {
            for(i=head[x];i;i=nxt[i]) {
                y=to[i];
                int l=lca(x,y);
                ans+=query(root[x],root[y],root[l],root[fa[l]],1,maxn,dfn[l],dfn[x]);
                ans+=query(root[x],root[y],root[l],root[fa[l]],1,maxn,dfn[l],dfn[y]);
                ans-=query(root[x],root[y],root[l],root[fa[l]],1,maxn,dfn[l],dfn[l]);
                ans--;
            }
        }
        ll tmp=gcd(ans,dev);
        printf("%lld/%lld
    ",ans/tmp,dev/tmp);
    }
    
  • 相关阅读:
    Linux中文显示乱码?如何设置centos显示中文
    查看mysql主从配置的状态及修正 slave不启动问题
    【Linux】Linux中的0644 和 0755的权限
    阿里云虚拟主机安装wordpress,提示连接数据库失败的解决方法
    neXtep 安装过程整理
    manjaro 设置开机启动脚本
    manjaro本地安装软件后添加快速启动到开始菜单
    k8s svc 添加 debug 端口
    为什么不建议在 MySQL 中使用 UTF-8?
    redis哨兵主从切换过程解析
  • 原文地址:https://www.cnblogs.com/suika/p/8967726.html
Copyright © 2020-2023  润新知