zoukankan      html  css  js  c++  java
  • BZOJ3772 精神污染 【主席树 + dfs序】

    题目

    兵库县位于日本列岛的中央位置,北临日本海,南面濑户内海直通太平洋,中央部位是森林和山地,与拥有关西机场的大阪府比邻而居,是关西地区面积最大的县,是集经济和文化于一体的一大地区,是日本西部门户,海陆空交通设施发达。濑户内海沿岸气候温暖,多晴天,有日本少见的贸易良港神户港所在的神户市和曾是豪族城邑“城下町”的姬路市等大城市,还有以疗养地而闻名的六甲山地等。
    兵库县官方也大力发展旅游,为了方便,他们在县内的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

    题解

    真是精神污染= =
    我们求出有多少对路径存在包含关系即可

    对于路径A,如果路径B的左右端点都在A的路径上,那么A包含B

    我们对每个路径左端点开一个表,储存其右端点

    这样我们只需要查询每个路径上点的右端点同时也在路径上的个数

    可以用dfs序 + 主席树,每个点u对应的主席树代表着到根节点的右端点的信息。
    在更新u时,先继承其父亲版本,然后对于u对应的所有右端点v,在v入度处+1,出序处-1
    这样一来对于u和其祖先v,两点之间的有效节点数 = 两点入度之间的值之和
    因为假若有一点不在这条路径上,却夹在两点入度之间,那么一定是出入度都在其中,一加一减就没了
    如果在这条路径上, 一定只有入度,还没到出度

    #include<iostream>
    #include<cmath>
    #include<cstdio>
    #include<vector>
    #include<cstring>
    #include<algorithm>
    #define LL long long int
    #define REP(i,n) for (int i = 1; i <= (n); i++)
    #define Redge(u) for (int k = h[u],to; k; k = ed[k].nxt)
    #define BUG(s,n) for (int i = 1; i <= (n); i++) cout<<s[i]<<' '; puts("");
    using namespace std;
    const int maxn = 100005,maxm = 4000005,INF = 1000000000;
    inline int read(){
    	int out = 0,flag = 1; char c = getchar();
    	while (c < 48 || c > 57) {if (c == '-') flag = -1; c = getchar();}
    	while (c >= 48 && c <= 57) {out = (out << 3) + (out << 1) + c - '0'; c = getchar();}
    	return out * flag;
    }
    int n,m,dfn[maxn],dft[maxn],fa[maxn][18],dep[maxn],cnt,h[maxn],ne = 2;
    vector<int> end[maxn];
    struct Que{int u,v;}q[maxn];
    inline bool operator <(const Que& a,const Que& b){
    	return a.u == b.u ? a.v < b.v : a.u < b.u;
    }
    struct EDGE{int to,nxt;}ed[2 * maxn];
    void build(int u,int v){
    	ed[ne] = (EDGE){v,h[u]}; h[u] = ne++;
    	ed[ne] = (EDGE){u,h[v]}; h[v] = ne++;
    }
    int rt[maxn],siz,sum[maxm],ls[maxm],rs[maxm];
    int update(int pre,int l,int r,int pos,int v){
    	int u = ++siz; ls[u] = ls[pre]; rs[u] = rs[pre];
    	if (l == r) {sum[u] = sum[pre] + v; return u;}
    	int mid = l + r >> 1;
    	if (mid >= pos) ls[u] = update(ls[pre],l,mid,pos,v);
    	else rs[u] = update(rs[pre],mid + 1,r,pos,v);
    	sum[u] = sum[ls[u]] + sum[rs[u]];
    	return u;
    }
    int query(int a,int b,int c,int d,int l,int r,int L,int R){
    	if (l >= L && r <= R) return sum[a] + sum[b] - sum[c] - sum[d];
    	int mid = l + r >> 1;
    	if (mid >= R) return query(ls[a],ls[b],ls[c],ls[d],l,mid,L,R);
    	else if (mid < L) return query(rs[a],rs[b],rs[c],rs[d],mid + 1,r,L,R);
    	else return query(ls[a],ls[b],ls[c],ls[d],l,mid,L,R) + query(rs[a],rs[b],rs[c],rs[d],mid + 1,r,L,R);
    }
    void dfs1(int u){
    	dfn[u] = ++cnt;
    	REP(i,17) fa[u][i] = fa[fa[u][i - 1]][i - 1];
    	Redge(u) if ((to = ed[k].to) != fa[u][0]){
    		fa[to][0] = u; dep[to] = dep[u] + 1; dfs1(to);
    	}
    	dft[u] = ++cnt;
    }
    void dfs2(int u){
    	rt[u] = rt[fa[u][0]];
    	for (unsigned int i = 0; i < end[u].size(); i++){
    		rt[u] = update(rt[u],1,cnt,dfn[end[u][i]],1);
    		rt[u] = update(rt[u],1,cnt,dft[end[u][i]],-1);
    	}
    	Redge(u) if ((to = ed[k].to) != fa[u][0]) dfs2(to);
    }
    int Lca(int u,int v){
    	if (dep[u] < dep[v]) swap(u,v);
    	for (int i = 0,d = dep[u] - dep[v]; (1 << i) <= d; i++)
    		if ((1 << i) & d) u = fa[u][i];
    	if (u == v) return u;
    	for (int i = 17; i >= 0; i--)
    		if (fa[u][i] != fa[v][i]) u = fa[u][i],v = fa[v][i];
    	return fa[u][0];
    }
    int solve(int u,int v,int lca){
    	int o = fa[lca][0],ans = 0;
    	ans += query(rt[u],rt[v],rt[lca],rt[o],1,cnt,dfn[lca],dfn[u]);
    	ans += query(rt[u],rt[v],rt[lca],rt[o],1,cnt,dfn[lca],dfn[v]);
    	ans -= query(rt[u],rt[v],rt[lca],rt[o],1,cnt,dfn[lca],dfn[lca]);
    	return ans - 1;
    }
    LL gcd(LL a,LL b){return !b ? a : gcd(b,a % b);}
    int main(){
    	n = read(); m = read();
    	REP(i,n - 1) build(read(),read());
    	REP(i,m) q[i].u = read(),q[i].v = read(),end[q[i].u].push_back(q[i].v);
    	sort(q + 1,q + 1 + m);
    	dfs1(1); dfs2(1);
    	LL ans = 0,D = (LL)m * (m - 1) >> 1;
    	REP(i,m) ans += solve(q[i].u,q[i].v,Lca(q[i].u,q[i].v));
    	LL d = gcd(ans,D);
    	printf("%lld/%lld
    ",ans / d,D / d);
    	return 0;
    }
    
    
  • 相关阅读:
    UI: 多窗口
    UI: 标题栏
    控件 UI: 字体的自动继承的特性, Style, ControlTemplate
    控件 UI: VisualState, VisualStateManager, 控件的默认 UI
    控件 UI: StateTrigger
    atoi、stoi、strtoi区别
    python下载IGS观测数据
    稳健估计
    间接平差程序实现
    android中的文件操作详解以及内部存储和外部存储(转载)
  • 原文地址:https://www.cnblogs.com/Mychael/p/8320543.html
Copyright © 2011-2022 走看看