zoukankan      html  css  js  c++  java
  • @loj


    @description@

    九条可怜是一个喜欢玩游戏的女孩子。为了增强自己的游戏水平,她想要用理论的武器武装自己。这道题和著名的 Minimax 搜索有关。

    可怜有一棵有根树,根节点编号为 1。定义根节点的深度为 1,其他节点的深度为它的父亲的深度加一。同时在叶子节点权值给定的情况下,可怜用如下方式定义了每一个非节点的权值:

    对于深度为奇数的非叶子节点,它的权值是它所有子节点的权值最大值。
    对于深度为偶数的非叶子节点,它的权值是它所有子节点的权值最小值。

    最开始,可怜令编号为 i 的叶子节点权值为 i,并计算得到了根节点的权值为 W。

    现在,邪恶的 Cedyks 想要通过修改某些叶子节点的权值,来让根节点的权值发生改变。Cedyks 设计了一个量子攻击器,在攻击器发动后,Cedyks 会随机获得一个非空的叶子节点集合 S 的控制权,并可以花费一定的能量来修改 S 中的叶子节点的权值。

    然而,修改叶子节点的权值也要消耗能量,对于 S 中的叶子节点 i,它的初始权值为 i,假设 Cedyks 把它的权值修改成了 wi(wi 可以是任意整数,包括负数),则 Cedyks 在这次攻击中,需要花费的能量为 (max_{iin S}|i - w_i|)

    Cedyks 想要尽可能节约能量,于是他总是会以最少的能量来完成攻击,即在花费的能量最小的情况下,让根节点的权值发生改变。令 w(S) 为 Cedyks 在获得了集合 S 的控制权后,会花费的能量。特殊地,对于某些集合 S,可能无论如何改变 S 中叶子节点的权值,根节点的权值都不会发生改变,这时,w(S) 的值被定义为 n。为了方便,我们称 w(S) 为 S 的稳定度。

    当有 m 个叶子节点的时候,一共有 2^m-1 种不同的叶子节点的非空集合。在发动攻击前,Cedyks 想要先预估一下自己需要花费的能量。于是他给出了一个区间 [L, R],他想要知道对于每一个 k ∈ [L, R],有多少个集合 S 满足 w(S) = k。

    答案可能会很大,请对 998244353 取模后输出。

    数据范围与提示
    对于 100% 的数据,保证 2 <= n <= 200000, 1 <= L <= R <= n。

    @solution@

    如果给定稳定度 k,我们可以得到每个叶子所能够更改的权值范围。
    但是可能不存在 i 使得 (max_{iin S}|i - w_i| = k),不符合稳定度的定义,
    我们求 ans[k] 表示所有 i 都满足 (max_{iin S}|i - w_i| le k) 的集合数量,最后作个差分。

    假设给定叶子集合 S,想要最大可能地改变根的值,必然要把 S 中的叶子取所能够取的最大值与最小值。
    设根的权值为 w,如果改成 min 可能导致根权 < w,如果改成 max 可能导致根权 > w。

    于是就可以想到一个 dp:定义 dp[0/1][i] 表示 i 这棵子树内有多少叶子集合,将叶子权值改成 min/max 后 i 的权值 <w / >w。最后得到 dp[0][rt] + dp[1][rt] 为答案。
    但是有一个问题,某些集合可能取 min 也合法,取 max 也合法,就会算重。

    注意到改成 min 时,小于 w 的那些叶结点其实根本不用改变(它改变了也没有用,因为它肯定是直接或间接被 w 在某个 max 结点淘汰掉)。
    同理,改成 max 时,大于 w 的那些叶结点也不需要改变。

    但是对于等于 w 的叶结点,它既可能变大也可能变小。
    注意到等于 w 的只有一个,如果一个叶子集合包含 w,它的稳定度必然为 1(直接改 =w 的权值,根结点自然就改变了)。
    所以我们可以强制叶子集合不包含 w,最后再加包含 w 的贡献。

    我们记 f[x] 表示只把 >w 的改成 min 是否 <w,记 g[x] 表示只把 <w 的改成 max 是否 >w。可以作两次 dp 求出 f,g。
    最后可以根据 <w 的叶子数量与 >w 的叶子数量算出答案。

    每一次 dp 都是 O(n) 复杂度,可是我们要求 [L, R] 中的所有 dp 值。
    注意 i 与 i+1 对应的情况只有最多两个叶子会改变,直接动态 dp 即可。

    @accepted code@

    #include <cstdio>
    #include <algorithm>
    using namespace std;
    const int MAXN = 200000;
    const int MOD = 998244353;
    struct mint{
    	int x;
    	mint(int _x=0) : x(_x) {}
    	friend mint operator + (mint a, mint b) {return a.x + b.x >= MOD ? a.x + b.x - MOD : a.x + b.x;}
    	friend mint operator - (mint a, mint b) {return a.x - b.x < 0 ? a.x - b.x + MOD : a.x - b.x;}
    	friend mint operator * (mint a, mint b) {return 1LL * a.x * b.x % MOD;}
    	friend void operator *= (mint &a, mint b) {a = a * b;}
    	friend void operator /= (mint &a, mint b) {
    		int p = MOD - 2;
    		while( p ) {
    			if( p & 1 ) a *= b;
    			b *= b;
    			p >>= 1;
    		}
    	}
    };
    struct matrix{
    	mint m[3][3];
    	matrix() {
    		for(int i=0;i<3;i++)
    			for(int j=0;j<3;j++)
    				m[i][j] = 0;
    	}
    	friend matrix operator * (matrix A, matrix B) {
    		matrix C;
    		C.m[0][0] = A.m[0][0]*B.m[0][0] + A.m[0][1]*B.m[1][0] + A.m[0][2]*B.m[2][0];
    		C.m[0][1] = A.m[0][0]*B.m[0][1] + A.m[0][1]*B.m[1][1] + A.m[0][2]*B.m[2][1];
    		C.m[0][2] = A.m[0][0]*B.m[0][2] + A.m[0][1]*B.m[1][2] + A.m[0][2]*B.m[2][2];
    		C.m[1][0] = A.m[1][0]*B.m[0][0] + A.m[1][1]*B.m[1][0] + A.m[1][2]*B.m[2][0];
    		C.m[1][1] = A.m[1][0]*B.m[0][1] + A.m[1][1]*B.m[1][1] + A.m[1][2]*B.m[2][1];
    		C.m[1][2] = A.m[1][0]*B.m[0][2] + A.m[1][1]*B.m[1][2] + A.m[1][2]*B.m[2][2];
    		C.m[2][0] = A.m[2][0]*B.m[0][0] + A.m[2][1]*B.m[1][0] + A.m[2][2]*B.m[2][0];
    		C.m[2][1] = A.m[2][0]*B.m[0][1] + A.m[2][1]*B.m[1][1] + A.m[2][2]*B.m[2][1];
    		C.m[2][2] = A.m[2][0]*B.m[0][2] + A.m[2][1]*B.m[1][2] + A.m[2][2]*B.m[2][2];
    		return C;
    	}
    }I;
    mint pw2[MAXN + 5];
    void init() {
    	pw2[0] = 1;
    	for(int i=1;i<=MAXN;i++)
    		pw2[i] = 2*pw2[i-1];
    	I.m[0][0] = I.m[1][1] = I.m[2][2] = 1;
    }
    struct edge{
    	int to; edge *nxt;
    }edges[2*MAXN + 5], *adj[MAXN + 5], *ecnt = edges;
    void addedge(int u, int v) {
    	edge *p = (++ecnt);
    	p->to = v, p->nxt = adj[u], adj[u] = p;
    	p = (++ecnt);
    	p->to = u, p->nxt = adj[v], adj[v] = p;
    }
    int val[MAXN + 5], dep[MAXN + 5];
    int fa[MAXN + 5], siz[MAXN + 5], hvy[MAXN + 5];
    void dfs1(int x, int f) {
    	fa[x] = f, siz[x] = 1, hvy[x] = 0, dep[x] = dep[f] + 1;
    	for(edge *p=adj[x];p;p=p->nxt) {
    		if( p->to == f ) continue;
    		dfs1(p->to, x), siz[x] += siz[p->to];
    		val[x] = (val[x] ? ((dep[x] & 1) ? max(val[x], val[p->to]) : min(val[x], val[p->to])) : val[p->to]);
    		if( siz[hvy[x]] < siz[p->to] )
    			hvy[x] = p->to;
    	}
    	if( !hvy[x] ) val[x] = x;
    }
    struct node{
    	int z; mint x;
    	node(int _x=0) : z(0), x(_x) {}
    	friend void operator *= (node &a, mint b) {
    		if( b.x == 0 ) a.z++;
    		else a.x *= b;
    	}
    	friend void operator /= (node &a, mint b) {
    		if( b.x == 0 ) a.z--;
    		else a.x /= b;
    	}
    	mint key() {return z ? 0 : x;}
    }a[MAXN + 5], b[MAXN + 5];
    mint f[MAXN + 5], g[MAXN + 5];
    /*
    f -> (only modify leaf > v1, vi < w)
    g -> (only modify leaf < v1, vi > w)
    */
    int lc1[MAXN + 5], lc2[MAXN + 5]; 
    int top[MAXN + 5], dfn[MAXN + 5], tid[MAXN + 5], btm[MAXN + 5], dcnt;
    int d;
    int ff(int x) {return (x > val[1] && x - d < val[1]) + (x < val[1]);}
    int gg(int x) {return (x < val[1] && x + d > val[1]) + (x > val[1]);}
    //选入集合 + 不选入集合
    int dfs2(int x, int tp) {
    	top[x] = tp, dfn[++dcnt] = x, tid[x] = dcnt;
    	if( !hvy[x] ) {
    /*
    		f[x] = (x > val[1] && x - 0 < val[1]) + (x < val[1]);
    		g[x] = (x < val[1] && x + 0 > val[1]) + (x > val[1]);
    */
    		f[x] = ff(x), g[x] = gg(x);
    		lc1[x] += (x > val[1]), lc2[x] += (x < val[1]);
    		return btm[x] = x;
    	}
    	btm[x] = dfs2(hvy[x], tp), lc1[x] += lc1[hvy[x]], lc2[x] += lc2[hvy[x]];
    	for(edge *p=adj[x];p;p=p->nxt)
    		if( p->to != hvy[x] && p->to != fa[x] )
    			dfs2(p->to, p->to), lc1[x] += lc1[p->to], lc2[x] += lc2[p->to];
    	if( dep[x] & 1 ) {
    		a[x] = b[x] = 1;
    		for(edge *p=adj[x];p;p=p->nxt)
    			if( p->to != hvy[x] && p->to != fa[x] )
    				a[x] *= f[p->to], b[x] *= (pw2[lc2[p->to]] - g[p->to]);
    		f[x] = a[x].key()*f[hvy[x]];
    		g[x] = pw2[lc2[x]] - b[x].key()*pw2[lc2[hvy[x]]] + b[x].key()*g[hvy[x]];
    	}
    	else {
    		a[x] = b[x] = 1;
    		for(edge *p=adj[x];p;p=p->nxt)
    			if( p->to != hvy[x] && p->to != fa[x] )
    				a[x] *= g[p->to], b[x] *= (pw2[lc1[p->to]] - f[p->to]);
    		g[x] = a[x].key()*g[hvy[x]];
    		f[x] = pw2[lc1[x]] - b[x].key()*pw2[lc1[hvy[x]]] + b[x].key()*f[hvy[x]];
    	}
    	return btm[x];
    }
    matrix getM(int x) {
    	if( !hvy[x] ) return I;
    	matrix M;
    	if( dep[x] & 1 ) {
    		M.m[0][0] = a[x].key(), M.m[1][1] = b[x].key();
    		M.m[1][2] = pw2[lc2[x]] - b[x].key()*pw2[lc2[hvy[x]]];
    		M.m[2][2] = 1;
    	}
    	else {
    		M.m[1][1] = a[x].key(), M.m[0][0] = b[x].key();
    		M.m[0][2] = pw2[lc1[x]] - b[x].key()*pw2[lc1[hvy[x]]];
    		M.m[2][2] = 1;
    	}
    	return M;
    }
    struct segtree{
    	#define lch (x << 1)
    	#define rch (x << 1 | 1)
    	struct node{
    		int le, ri;
    		matrix M;
    	}t[4*MAXN + 5];
    	void pushup(int x) {t[x].M = t[lch].M * t[rch].M;}
    	void build(int x, int l, int r) {
    		t[x].le = l, t[x].ri = r;
    		if( l == r ) {
    			t[x].M = getM(dfn[l]);
    			return ;
    		}
    		int m = (l + r) >> 1;
    		build(lch, l, m), build(rch, m + 1, r);
    		pushup(x);
    	}
    	void update(int x, int p) {
    		if( p > t[x].ri || p < t[x].le )
    			return ;
    		if( t[x].le == t[x].ri ) {
    			t[x].M = getM(dfn[p]);
    			return ;
    		}
    		update(lch, p), update(rch, p);
    		pushup(x);
    	}
    	matrix query(int x, int l, int r) {
    		if( l > t[x].ri || r < t[x].le )
    			return I;
    		if( l <= t[x].le && t[x].ri <= r )
    			return t[x].M;
    		return query(lch, l, r) * query(rch, l, r);
    	}
    }T;
    void modify(int x) {
    	while( fa[top[x]] ) {
    		x = top[x];
    		if( dep[fa[x]] & 1 )
    			a[fa[x]] /= f[x], b[fa[x]] /= (pw2[lc2[x]] - g[x]);
    		else a[fa[x]] /= g[x], b[fa[x]] /= (pw2[lc1[x]] - f[x]);
    		matrix M = T.query(1, tid[x], tid[btm[x]]);
    		f[x] = M.m[0][0]*ff(btm[x]) + M.m[0][2];
    		g[x] = M.m[1][1]*gg(btm[x]) + M.m[1][2];
    		if( dep[fa[x]] & 1 )
    			a[fa[x]] *= f[x], b[fa[x]] *= (pw2[lc2[x]] - g[x]);
    		else a[fa[x]] *= g[x], b[fa[x]] *= (pw2[lc1[x]] - f[x]);
    		x = fa[x];
    		T.update(1, tid[x]);
    	}
    }
    mint get_ans() {
    	matrix M = T.query(1, tid[1], tid[btm[1]]);
    	mint f1 = M.m[0][0]*ff(btm[1]) + M.m[0][2];
    	mint g1 = M.m[1][1]*gg(btm[1]) + M.m[1][2];
    	return pw2[lc1[1] + lc2[1]] - (pw2[lc1[1]] - f1) * (pw2[lc2[1]] - g1);
    }
    int n, L, R;
    mint ans[MAXN + 5];
    int read() {
    	int x = 0; char ch = getchar();
    	while( ch > '9' || ch < '0' ) ch = getchar();
    	while( '0' <= ch && ch <= '9' ) x = 10*x + ch - '0', ch = getchar();
    	return x;
    }
    void write(int x) {
    	if( !x ) return ;
    	write(x/10);
    	putchar(x%10 + '0');
    }
    int main() {
    	init(), n = read(), L = read(), R = read();
    	for(int i=1;i<n;i++) {
    		int u = read(), v = read();
    		addedge(u, v);
    	}
    	dfs1(1, 0), dfs2(1, 1), T.build(1, 1, n);
    	for(d=1;d<=n;d++) {
    		int p = (val[1] + 1 - d);
    		if( p < val[1] && p >= 1 && !hvy[p] )
    			modify(p);
    		p = (val[1] - 1 + d);
    		if( p > val[1] && p <= n && !hvy[p] )
    			modify(p);
    		ans[d] = get_ans();
    	}
    	ans[n] = pw2[lc1[1]+lc2[1]] - ans[n] - 1;
    	for(int i=n-1;i>=1;i--) ans[i] = ans[i] - ans[i-1];
    	ans[1] = ans[1] + pw2[lc1[1]+lc2[1]];
    	for(int i=L;i<=R;i++) {
    		if( ans[i].x ) write(ans[i].x); else putchar('0');
    		putchar(i == n ? '
    ' : ' ');
    	}
    }
    

    @details@

    所以我并不知道为什么本地测大样例要跑 5s 交上去竟然 AC 了。

  • 相关阅读:
    测试用例设计方法——等价类划分法
    软件测试方法--错误推测法
    测试用例设计方法--因果图法
    测试用例设计方法--判定表法
    测试用例设计方法--边界值分析法
    测试用例设计方法--场景法
    软件测试分类介绍
    软件测试模型
    软件开发过程模型
    m3u8视频流下载方案
  • 原文地址:https://www.cnblogs.com/Tiw-Air-OAO/p/11915347.html
Copyright © 2011-2022 走看看