zoukankan      html  css  js  c++  java
  • 【ybtoj高效进阶 21252】逛动物园(线段树)(dfs)

    逛动物园

    题目链接:ybtoj高效进阶 21252

    题目大意

    给你 n 个点,值可能是 a,b,c(等概率),然后有以下规则:a 优于 b,b 优于 c,c 优于 a。
    然后要你处理若干次操作:把两个点放在一起,保留优的点。然后问你有多少的概率使得 x 点到现在还存在。

    思路

    首先考虑赢的概率,不难发现本场赢的是 (frac{2}{3}),新来的赢的概率是 (frac{1}{3})

    然后你发现你可以把 (v) 跑到 (u) 当做是 (v)(u) 连边,然后就会是一个树的结构。

    然后你考虑如果一个点连向另一个点,它这个点的子树里面的点的概率都要乘上。
    那就是树一个区间的搞,然后你考虑预先把最后的树建出来,然后用 dfs 序来线段树维护。

    然后注意的就是存边的时候用 vector,因为你要正向的枚举边才能使得每次你要修改的都是一段连续的区间。

    然后询问就是简单的单点查询,就好啦。

    代码

    #include<cstdio>
    #include<vector>
    #define ll long long
    #define mo 998244353
    
    using namespace std;
    
    int n, m, op[200001], x[200001], y[200001], fa[200001];
    int sz[200001], dfn[200001], tmp, pl[200001];
    ll a[800001], lzy[800001];
    vector <int> e[200001];
    //ll ans[200001];
    
    ll ksm(ll x, ll y) {
    	ll re = 1;
    	while (y) {
    		if (y & 1) re = re * x % mo;
    		x = x * x % mo;
    		y >>= 1;
    	}
    	return re;
    }
    
    ll inv(ll x) {
    	return ksm(x, mo - 2);
    }
    
    void dfs(int now) {
    	dfn[now] = ++tmp;
    	pl[tmp] = now;
    	for (int i = 0; i < e[now].size(); i++)
    		dfs(e[now][i]);
    }
    
    void build(int now, int l, int r) {//线段树
    	a[now] = 1;
    	lzy[now] = 1;
    	if (l == r) return ;
    	int mid = (l + r) >> 1;
    	build(now << 1, l, mid);
    	build(now << 1 | 1, mid + 1, r);
    }
    
    void down(int now) {
    	if (lzy[now] == 1) return ;
    	lzy[now << 1] = lzy[now << 1] * lzy[now] % mo;
    	lzy[now << 1 | 1] = lzy[now << 1 | 1] * lzy[now] % mo;
    	a[now << 1] = a[now << 1] * lzy[now] % mo;
    	a[now << 1 | 1] = a[now << 1 | 1] * lzy[now] % mo;
    	lzy[now] = 1;
    }
    
    void insert(int now, int l, int r, int L, int R, int val) {
    	if (L <= l && r <= R) {
    		a[now] = a[now] * val % mo;
    		lzy[now] = lzy[now] * val % mo;
    		return ;
    	}
    	
    	down(now);
    	int mid = (l + r) >> 1;
    	if (L <= mid) insert(now << 1, l, mid, L, R, val);
    	if (mid < R) insert(now << 1 | 1, mid + 1, r, L, R, val);
    }
    
    int query(int now, int l, int r, int pl) {
    	if (l == r) return a[now];
    	
    	down(now);
    	int mid = (l + r) >> 1;
    	if (pl <= mid) return query(now << 1, l, mid, pl);
    		else return query(now << 1 | 1, mid + 1, r, pl);
    }
    
    int main() {
    //	freopen("zoo.in", "r", stdin);
    //	freopen("zoo.out", "w", stdout);
    	
    	scanf("%d %d", &n, &m);
    	
    	ll a23 = 2ll * inv(3) % mo, a13 = inv(3), all = ksm(3, n);
    	for (int i = 1; i <= m; i++) {//提前建出数的结构
    		scanf("%d", &op[i]);
    		if (op[i] == 1) {
    			scanf("%d %d", &x[i], &y[i]);
    			e[x[i]].push_back(y[i]);
    			fa[y[i]] = x[i];
    		}
    		else {
    			scanf("%d", &x[i]);
    		}
    	}
    	
    	for (int i = 1; i <= n; i++) {
    		sz[i] = 1;
    		if (!fa[i]) dfs(i);
    	}
    	
    	build(1, 1, n);
    	for (int i = 1; i <= m; i++)
    		if (op[i] == 1) {
    			insert(1, 1, n, dfn[x[i]], dfn[x[i]] + sz[x[i]] - 1, a23);//本场赢的概率是 2/3
    			insert(1, 1, n, dfn[y[i]], dfn[y[i]] + sz[y[i]] - 1, a13);//新来的赢的概率是 1/3
    			sz[x[i]] += sz[y[i]];
    		}
    		else {
    			printf("%lld
    ", all * query(1, 1, n, dfn[x[i]]) % mo);
    		}
    	
    	return 0;
    }
    
    
  • 相关阅读:
    bzoj1951 [Sdoi2010]古代猪文
    bzoj2693 jzptab
    数学一本通第三章总结
    poj1019 Number Sequence
    SGU179 Brackets light
    字母组合2
    字母组合
    Java基础知识强化之集合框架笔记09:Collection集合迭代器使用的问题探讨
    Java基础知识强化之集合框架笔记08:Collection集合自定义对象并遍历案例(使用迭代器)
    Java基础知识强化之集合框架笔记07:Collection集合的遍历之迭代器遍历
  • 原文地址:https://www.cnblogs.com/Sakura-TJH/p/YBTOJ_GXJJ_21252.html
Copyright © 2011-2022 走看看