zoukankan      html  css  js  c++  java
  • 【题解】树上的鼠

    博弈策略

    首先考虑一条链的情况。

    如果链长是奇数,而开始鼠在中点,则无论往哪里跳,对手都可以跳到对称点,因为距离限制,只会越来越靠外,最后到达边界先手就输了。否则只要第一手跳到中点即可。

    如果链长是偶数,则策略与奇数相同,只是没有中点了,无论开始在哪里,只要跳到中间的两个点,先手就赢了。

    树上方法是一样的,只要在直径上跳就可以了,不跳直径只会增加长度限制,所以每次都跳回到直径上就可以了。

    因此先手必败的条件就是(1)号在长度为奇数的直径中点上,可以转化为赢的条件就是(1)子树中最深的深度只出现一次。

    DP与优化

    (x)子树,最大深度(leq i),方案数为(f(x, i))

    (v)(x)的孩子,(len(v))表示(v)的最大深度

    [f(x, i)= egin{cases} f'(x,i) cdot (1+f(v, i-1)) & i-1<len(v) & (1) \ f'(x,i) cdot (1+f(v, len(v)-1) & i-1 geq len(v) & (2) end{cases} ]

    (+1)就是不选这棵子树。然后与深度有关,用长链剖分优化。

    注意方程中更新时要更新到(len(x)-1),但实际上第二种情况就是一个后缀乘法,因此可以打上标记。

    同时对于每个点,新增时会增加一个(dep=0)的情况,此时要把数组整体(+1),也打标记(因为(f)是个前缀和,方便转移),这样可以保证复杂度(mathcal O(n))

    如果不加限制,这样的两种操作差分标记是维护不了的,但这题因为是DP,每次都是按顺序连续进行的,可以在DP更新当前位置前把标记推到下一个位置上,这样就可以保证所有标记都被按时推走了。

    统计答案

    算出所有最深子树出现(1)次的方案相加就可以了。

    这里做法是枚举最大深度,然后维护一个std::list(active)表示深度达到当前深度的元素,先做出前后缀积,然后每次枚举一个元素,它的深度必须是(dep),其他的深度小于(dep),乘起来。最后把(len<dep)的(不能作为最深的子树的)乘到(inactive)里面,并从(active)删掉即可,这块代码写的比较清楚。

    Code

    #include <cstdio>
    #include <cctype>
    #include <cstring>
    #include <algorithm>
    #include <list>
    using namespace std;
    #define File(s) freopen(s".in", "r", stdin), freopen(s".out", "w", stdout)
    typedef long long ll;
    const int inv2 = 499122177;
    namespace io {
    	const int SIZE = (1 << 21) + 1;
    	char ibuf[SIZE], *iS, *iT, obuf[SIZE], *oS = obuf, *oT = oS + SIZE - 1, c, qu[55]; int f, qr;
    	#define gc() (iS == iT ? (iT = (iS = ibuf) + fread (ibuf, 1, SIZE, stdin), (iS == iT ? EOF : *iS ++)) : *iS ++)
    	char getc () {return gc();}
    	inline void flush () {fwrite (obuf, 1, oS - obuf, stdout); oS = obuf;}
    	inline void putc (char x) {*oS ++ = x; if (oS == oT) flush ();}
    	template <class I> inline void gi (I &x) {for (f = 1, c = gc(); c < '0' || c > '9'; c = gc()) if (c == '-') f = -1;for (x = 0; c <= '9' && c >= '0'; c = gc()) x = x * 10 + (c & 15); x *= f;}
    	template <class I> inline void print (I x) {if (!x) putc ('0'); if (x < 0) putc ('-'), x = -x;while (x) qu[++ qr] = x % 10 + '0',  x /= 10;while (qr) putc (qu[qr --]);}
    	struct Flusher_ {~Flusher_(){flush();}}io_flusher_;
    }
    using io :: gi; using io :: putc; using io :: print; using io :: getc;
    template<class T> void upmax(T &x, T y){x = x>y ? x : y;}
    template<class T> void upmin(T &x, T y){x = x<y ? x : y;}
    
    const int N = 1000005, E = 2000005, p = 998244353;
    inline int add(int x, int y){return x+y>=p ? x+y-p : x+y;}
    inline int sub(int x, int y){return x-y<0 ? x-y+p : x-y;}
    inline int mul(int x, int y){return 1LL * x * y % p;}
    inline int power(int x, int y){
    	int res = 1;
    	for(; y; y>>=1, x = mul(x, x)) if(y & 1) res = mul(res, x);
    	return res;
    }
    int hd[N], to[E], nt[E], ec = 0;
    inline void link(int x, int y){
    	++ec;
    	to[ec] = y; nt[ec] = hd[x]; hd[x] = ec;
    }
    
    int son[N], len[N];
    struct Dp{
    	int val, tmul, tadd;
    	Dp():val(0), tmul(1), tadd(0){}
    };
    Dp tp[N], *pt = tp, *f[N];
    
    void build(int x, int fa){
    	for(int i=hd[x]; i; i=nt[i]){
    		if(to[i] == fa) continue;
    		build(to[i], x);
    		if(len[to[i]] > len[son[x]]) son[x] = to[i];
    	}
    	len[x] = len[son[x]] + 1;
    }
    
    void push(int x, int i){
    	f[x][i].val = mul(f[x][i].val, f[x][i].tmul);
    	f[x][i].val = add(f[x][i].val, f[x][i].tadd);
    	if(i + 1 < len[x]){
    		f[x][i+1].tmul = mul(f[x][i+1].tmul, f[x][i].tmul);
    		f[x][i+1].tadd = add(mul(f[x][i+1].tadd, f[x][i].tmul), f[x][i].tadd);
    	}
    	f[x][i].tadd = 0;
    	f[x][i].tmul = 1;
    }
    
    void dfs(int x, int fa){
    	f[x][0].val = 1;
    	if(!son[x]) return ;
    	f[son[x]] = f[x] + 1;
    	dfs(son[x], x);
    	f[x][1].tadd = add(f[x][1].tadd, 1);
    	for(int i=hd[x]; i; i=nt[i]){
    		if(to[i] == fa || to[i] == son[x]) continue;
    		int v = to[i];
    		f[v] = pt; pt += len[v];
    		dfs(v, x);
    		push(v, 0);
    		for(int i=1; i<len[v]; i++){
    			push(x, i); push(v, i);
    			f[x][i].val = mul(f[x][i].val, f[v][i-1].val + 1);
    		}
    		push(x, len[v]);
    		f[x][len[v]].tmul = mul(f[x][len[v]].tmul, f[v][len[v] - 1].val + 1);
    	}
    }
    
    int main(){
    	int n, x, y;
    	gi(n);
    	for(int i=1; i<n; i++) gi(x), gi(y), link(x, y), link(y, x);
    	build(1, 0);
    	int res = 0, cnt0 = 0;
    	list<int> active;
    	int inactive = 1;
    	for(int i=hd[1]; i; i=nt[i]){
    		int v = to[i];
    		f[v] = pt; pt += len[v];
    		dfs(v, 1);
    		++cnt0;
    		for(int i=0; i<len[v]; i++) push(v, i), ++f[v][i].val;
    		if(len[v] > 1) active.push_back(v);
    		else inactive = mul(inactive, f[v][len[v] - 1].val);
    	}
    	static int pre[N], suf[N];
    	for(int i=1; i<len[1]-1; i++){
    		int sz = active.size();
    		int p = 1;
    		pre[0] = 1;
    		for(list<int>::iterator it = active.begin(); p < sz; p++, it++)
    			pre[p] = mul(pre[p - 1], f[*it][i-1].val);
    		p = sz;
    		suf[sz + 1] = 1;
    		for(list<int>::reverse_iterator it = active.rbegin(); p >= 1; p--, it++){
    			suf[p] = mul(suf[p + 1], f[*it][i-1].val);
    			res = add(res, mul(mul(sub(f[*it][i].val, f[*it][i-1].val), mul(suf[p+1], pre[p-1])), inactive));
    		}
    		for(list<int>::iterator it = active.begin(); it != active.end(); ){
    			list<int>::iterator last = it; ++it;
    			if(len[*last] - 1 == i){
    				inactive = mul(inactive, f[*last][i].val);
    				active.erase(last);
    			}
    		}
    	}
    	printf("%d
    ", add(res, cnt0));
    	return 0;
    }
    
  • 相关阅读:
    20145224&20145238 《信息安全系统设计基础》第二次实验
    《信息安全系统设计基础》 第九周学习总结
    20145224&20145238 《信息安全系统设计基础》 第一次实验
    《信息安全系统设计基础》 第八周学习总结
    《信息安全系统设计基础》 第七周学习总结
    《信息安全系统设计基础》 第六周学习总结
    在Ubuntu中创建与Windows的共享文件夹
    《信息安全系统设计基础》 第五周学习总结
    《信息安全系统设计基础》 第三周学习总结
    20145211 《信息安全系统设计基础》第十周学习总结——水流无限似侬愁
  • 原文地址:https://www.cnblogs.com/RiverHamster/p/12293968.html
Copyright © 2011-2022 走看看