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

 

做过接水果这道题就简单了。
设A的两端点为x,y,B的两端点为u,v。

考虑一条路径A被另一条路径B包含的条件是:

当x=y时,u和v有一个应在x的子树中,另一个应在x的子树外或等于x。

当x、y构成了祖先关系时,设y为祖先、z为x到y路径上倒数第二个点,则u和v有一个应在x的子树中,另一个不在z的子树中。

当x、y不构成祖先关系时,则u和v有一个应在x的子树中,另一个在y的子树中。

那么一条路径可以变成至多2个矩形和一个点。

扫描线+树状数组计算一下有多少点在矩形中就可以了。

#include<cstdio>
#include<cctype>
#include<queue>
#include<cmath>
#include<cstring>
#include<algorithm>
#define rep(i,s,t) for(int i=s;i<=t;i++)
#define dwn(i,s,t) for(int i=s;i>=t;i--)
#define ren for(int i=first[x];i;i=next[i])
using namespace std;
const int BufferSize=1<<16;
char buffer[BufferSize],*head,*tail;
inline char Getchar() {
    if(head==tail) {
        int l=fread(buffer,1,BufferSize,stdin);
        tail=(head=buffer)+l;
    }
    return *head++;
}
inline int read() {
    int x=0,f=1;char c=Getchar();
    for(;!isdigit(c);c=Getchar()) if(c=='-') f=-1;
    for(;isdigit(c);c=Getchar()) x=x*10+c-'0';
    return x*f;
}
typedef long long ll;
const int maxn=100010;
int n,m,e,first[maxn],next[maxn<<1],to[maxn<<1];
void AddEdge(int u,int v) {
    to[++e]=v;next[e]=first[u];first[u]=e;
    to[++e]=u;next[e]=first[v];first[v]=e;
}
int anc[maxn][20],dep[maxn],st[maxn],en[maxn],ToT;
void dfs(int x,int fa) {
    anc[x][0]=fa;rep(i,1,19) anc[x][i]=anc[anc[x][i-1]][i-1];
    dep[x]=dep[fa]+1;st[x]=++ToT;
    ren if(to[i]!=fa) dfs(to[i],x);
    en[x]=ToT;
}
int swim(int x,int k) {
    rep(i,0,19) if(k>>i&1) x=anc[x][i];
    return x;
}
struct Rect {
    int x,l,r,val;
    bool operator < (const Rect& ths) const {return x<ths.x;}
}A[maxn<<2];
struct Point {
    int x,y;
    bool operator < (const Point& ths) const {return x<ths.x||(x==ths.x&&y<ths.y);}
}B[maxn];
int N,M;
void AddRect(int x,int y,int l,int r) {
    if(x>y||l>r) return;
    if(x<l) swap(x,l),swap(y,r);
    A[++N]=(Rect){x,l,r,1};A[++N]=(Rect){y+1,l,r,-1};
}
void AddPoint(int x,int y) {
    if(x<y) swap(x,y);
    B[++M]=(Point){x,y};
}
int sumv[maxn];
int query(int x) {int res=0;for(;x;x-=x&-x) res+=sumv[x];return res;}
void add(int x,int v) {for(;x<=n;x+=x&-x) sumv[x]+=v;}
ll ans;
ll gcd(ll a,ll b) {return !b?a:gcd(b,a%b);}
int main() {
    n=read();m=read();
    rep(i,2,n) AddEdge(read(),read());
    dfs(1,0);
    rep(i,1,m) {
        int u=read(),v=read();AddPoint(st[u],st[v]);
        if(dep[u]<dep[v]) swap(u,v);
        if(u==v) AddRect(st[u],en[u],1,st[u]),AddRect(st[u],en[u],en[u]+1,n);
        else if(st[u]>=st[v]&&st[u]<=en[v]) {
            v=swim(u,dep[u]-dep[v]-1);
            AddRect(st[u],en[u],1,st[v]-1);
            AddRect(st[u],en[u],en[v]+1,n);
        }
        else AddRect(st[u],en[u],st[v],en[v]);
    }
    sort(A+1,A+N+1);sort(B+1,B+M+1);int j=1;
    rep(i,1,M) {
        while(j<=N&&A[j].x<=B[i].x) add(A[j].l,A[j].val),add(A[j].r+1,-A[j].val),j++;
        ans+=query(B[i].y);
    }
    rep(i,1,M) {
        int j=i;
        while(j<=M&&B[j].x==B[i].x&&B[j].y==B[i].y) j++;j--;
        ans-=(ll)(j-i+1)*(j-i)/2;
        i=j;
    }
    ans-=m;ll ans2=(ll)m*(m-1)/2,c=gcd(ans,ans2);
    printf("%lld/%lld\n",ans/c,ans2/c);
    return 0;
}
View Code

 

posted @ 2016-03-02 19:02  wzj_is_a_juruo  阅读(196)  评论(0编辑  收藏  举报