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
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。
样例解释
可以选择的路径对有(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个矩形和一个点。
扫描线+树状数组计算一下有多少点在矩形中就可以了。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#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 ",ans/c,ans2/c); return 0; }