• loj2542. 「PKUWC2018」随机游走


    题意

    略。

    题解

    看到这个(n leq 18),就知道可能是容斥。
    结果是min-max容斥。
    原来要求一个点出发到一个点集最晚访问的点的期望时间,现在求的则是一个点出发到一个点集最早访问的点的期望时间。
    这个东西可以树上dp,而且显然dp出来的东西是可以高斯消元的。
    但是这样复杂度不对。
    考虑有个树上高消的方法,建立在线性关系的基础上。
    可以推一发式子:

    [f_i = frac{1}{d_i} (f_{{fa}_i} + sum_{j in {son}_i} f_j) + 1 ]

    并设

    [f_i = a_i f_{{fa}_i} + b_i ]

    (就不推了,懒)

    [f_i = frac{1}{d_i - sum_{j in {son}_i} a_j} f_{{fa}_i} + frac{sum_{j in {son}_i} b_j + d_i}{d_i - sum_{j in {son}_i} a_j} ]

    然后就可以从下到上回溯的时候(mathcal O(n log p))计算了。
    对于多组数据,其实可以直接预处理每种终点集合的答案,具体就是,终点集合里的每个点(i)(a_i, b_i, f_i)都先为0,然后以出发点为根dp,同时计算(a)(b)数组,则到这个子集的答案就是(f_{root})(即(b_{root})), 复杂度(mathcal O(2 ^ n n))
    然后询问的时候使用min-max容斥,枚举子集,每次(mathcal O(1))计算即可。
    总复杂度(mathcal O(2 ^ n n + nq))

    #include <bits/stdc++.h>
    using namespace std;
    
    const int N = 19, mod = 998244353;
    int n, q, rt, ans;
    int a[N], b[N], d[N], fa[N];
    int in[N], sgn[1 << N], mn[1 << N];
    vector <int> g[N];
    int power (int a, int b) {
    	int ret = 1;
    	for ( ; b; b >>= 1, a = 1ll * a * a % mod) {
    		if (b & 1) {
    			ret = 1ll * ret * a % mod;
    		}
    	}
    	return ret;
    }
    int inv (int x) {
    	return power(x, mod - 2);
    }
    void add (int x, int y) {
    	g[x].push_back(y), ++d[x];
    }
    void dfs (int x, int p) {
    	if (in[x]) {
    		a[x] = b[x] = 0;
    		return;
    	}
    	fa[x] = p, a[x] = b[x] = d[x];
    	for (auto y : g[x]) {
    		if (y != p) {
    			dfs(y, x);
    			a[x] = (a[x] - a[y] + mod) % mod;
    			b[x] = (b[x] + b[y]) % mod;
    		}
    	}
    	a[x] = inv(a[x]), b[x] = 1ll * b[x] * a[x] % mod;
    }
    int main () {
    	scanf("%d%d%d", &n, &q, &rt);
    	for (int i = 1, x, y; i < n; ++i) {
    		scanf("%d%d", &x, &y);
    		add(x, y), add(y, x);
    	}
    	for (int s = 1; s < (1 << n); ++s) {
    		sgn[s] = -1;
    		for (int i = 0; i < n; ++i) {
    			if (s >> i & 1) {
    				in[i + 1] = 1;
    				sgn[s] = -sgn[s];
    			} else {
    				in[i + 1] = 0;
    			}
    		}
    		dfs(rt, 0);
    		mn[s] = b[rt];
    	}
    	for (int k, x, s; q; --q) {
    		scanf("%d", &k), s = 0;
    		for (int i = 1; i <= k; ++i) {
    			scanf("%d", &x);
    			s |= 1 << (x - 1);
    		}
    		ans = 0;
    		for (int t = s; t; t = (t - 1) & s) {
    			ans = (ans + mn[t] * sgn[t]) % mod;
    		}
    		ans = (ans % mod + mod) % mod;
    		printf("%d
    ", ans);
    	}
    	return 0;
    }
    
  • 相关阅读:
    Oracle之数据库的增删改查和格式的修改
    Oracle之表的相关操作
    Oracle之现有表上建新表、操作符、字符函数
    Oracle之用户和表空间
    相关Linux命令
    克隆环境
    机器学习笔记(四)神经网络的基本概念
    机器学习作业(二)逻辑回归——Python(numpy)实现
    机器学习作业(二)逻辑回归——Matlab实现
    机器学习笔记(三)逻辑回归
  • 原文地址:https://www.cnblogs.com/psimonw/p/11442485.html
Copyright © 2020-2023  润新知