• 【LOJ】#6391. 「THUPC2018」淘米神的树 / Tommy


    题解

    一道非常神仙的计数题
    如果只有一个点,就是非常简单的树型dp

    (f_{u} = (siz_{u} - 1)! prod_{v in son_{u}} frac{f_{v}}{siz_{v}!})
    (frac{f_{u}}{siz_{u}!} = frac{1}{siz_{u}} prod_{v in son_{u}} frac{f_{v}}{siz_{v}!})
    (f_{u} = frac{n!}{prod s_{i}})

    可是我们有两个点,我们把这两个点连起来的点作为第一个黑点的话,这就是一个环套树,把环拎出来,我们枚举在哪里断开

    我们设环长为m,添加上的黑点即(v_0)同时(v_0)(v_m)相连

    这样的话,如果我们要让(v_{i})是最后一个被染红的,那么不在环上的点的子树大小都不会改变
    我们断开(v_{i} - v_{i + 1})(v_{i - 1} - v_{i})都可以
    也就是说,我们断开一条边,对应着两个点被最后一个染色的方案数
    总共计算了两遍,我们最后的答案除二就行

    现在,我们把问题转化成了,枚举断开的一条边,把图变成一棵树,求这棵树的方案数,即套用我们所求的公式,需要的就是快速算出环上的点的子树大小更新的情况

    我们用(s_{i})表示第i个点上所挂的树的大小,并把这个东西处理成一个前缀和
    我们对于每个点,它的值是
    (prod_{i eq j } (s_{i} - s_{j}))
    我们要求的是
    (sum_{i} prod_{i eq q} (s_{i} - s_{j}))
    我们如果把(s_{i})设成变量,那么
    (sum_{i} prod_{i eq q} (x- s_{j}))这个式子可以联系到导数
    也就是
    (sum_{i} prod_{i eq q} (x - s_{j}) = frac{mathrm{d} }{mathrm{d} x} prod (x - s_{i}))
    然后我们直接多项式插值就可以了

    代码

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <vector>
    #include <cmath>
    #include <queue>
    #include <ctime>
    #include <map>
    #include <set>
    #define fi first
    #define se second
    #define pii pair<int,int>
    //#define ivorysi
    #define mp make_pair
    #define pb push_back
    #define enter putchar('
    ')
    #define space putchar(' ')
    #define MAXN 234600
    using namespace std;
    typedef long long int64;
    typedef double db;
    typedef unsigned int u32;
    template<class T>
    void read(T &res) {
    	res = 0;T f = 1;char c = getchar();
    	while(c < '0' || c > '9') {
    		if(c == '-') f = -1;
    		c = getchar();
    	}
    	while(c >= '0' && c <= '9' ) {
    		res = res * 10 - '0' + c;
    		c = getchar();
    	}
    	res *= f;
    }
    template<class T>
    void out(T x) {
    	if(x < 0) {x = -x;putchar('-');}
    	if(x >= 10) {
    		out(x / 10);
    	}
    	putchar('0' + x % 10);
    }
    
    const int MOD = 998244353;
    int W[(1 << 20) + 5];
    int mul(int a,int b) {
    	return 1LL * a * b % MOD;
    }
    int inc(int a,int b) {
    	return a + b >= MOD ? a + b - MOD : a + b;
    }
    int fpow(int x,int c) {
    	int res = 1,t = x;
    	while(c) {
    		if(c & 1) res = mul(res,t);
    		t = mul(t,t);
    		c >>= 1;
    	}
    	return res;
    }
    struct node {
    	int to,next;
    }E[MAXN * 2];
    int head[MAXN],sumE,N,A,B,siz[MAXN],fa[MAXN],sum[MAXN],tot,fac,inv[MAXN];
    int ans[MAXN];
    void add(int u,int v) {
    	E[++sumE].to = v;
    	E[sumE].next = head[u];
    	head[u] = sumE;
    }
    void dfs(int u) {
    	siz[u] = 1;
    	for(int i = head[u] ; i ; i = E[i].next) {
    		int v = E[i].to;
    		if(v != fa[u]) {
    			fa[v] = u;
    			dfs(v);
    			siz[u] += siz[v];
    		}
    	}
    }
    struct poly {
    	vector<int> p;
    	poly() {
    		p.clear();
    	}
    	void print() {
    		for(int i = 0 ; i < p.size() ; ++i) {out(p[i]);space;}
    		enter;
    	}
    	friend void NTT(poly &f,int L,int on) {
    		f.p.resize(L);
    		for(int i = 1, j = L / 2 ; i < L - 1 ; ++i) {
    			if(i < j) swap(f.p[i],f.p[j]);
    			int k = L / 2;
    			while(j >= k) {
    				j -= k;
    				k >>= 1;
    			}
    			j += k;
    		}
    		for(int h = 2 ; h <= L ; h <<= 1) {
    			int wn = W[((1 << 20) + on * (1 << 20) / h) % (1 << 20)];
    			for(int k = 0 ; k < L ; k += h) {
    				int w = 1;
    				for(int j = k ; j < k + h / 2 ; ++j) {
    					int u = f.p[j],t = mul(w,f.p[j + h / 2]);
    					f.p[j] = inc(u,t);
    					f.p[j + h / 2] = inc(u,MOD - t);
    					w = mul(w,wn);
    				}
    			}
    		}
    		if(on == -1) {
    			int InvL = fpow(L,MOD - 2);
    			for(int i = 0 ; i < L ; ++i) {
    				f.p[i] = mul(f.p[i],InvL);
    			}
    		}
    	}
    	friend poly operator * (poly f,poly g) {
    		int L = f.p.size() + g.p.size();
    		int t = 1;
    		while(t <= L) t <<= 1;
    		poly h;h.p.resize(t);
    		NTT(f,t,1);NTT(g,t,1);
    		for(int i = 0 ; i < t ; ++i) {
    			h.p[i] = mul(f.p[i],g.p[i]);
    		}
    		NTT(h,t,-1);
    		for(int i = t - 1 ; i >= 0; --i) {
    			if(h.p[i] == 0) h.p.pop_back();
    			else break;
    		}
    		return h;
    	}
    	friend poly operator + (poly f,poly g) {
    		poly h;
    		int t = max(f.p.size(),g.p.size());
    		f.p.resize(t);g.p.resize(t);
    		for(int i = 0 ; i < t ; ++i) {
    			h.p.pb(inc(f.p[i],g.p[i]));
    		}
    		for(int i = t - 1; i >= 0 ; --i) {
    			if(!h.p[i]) h.p.pop_back();
    			else break;
    		}
    		return h;
    	}
    	friend poly operator - (poly f,poly g) {
    		poly h;
    		int t = max(f.p.size(),g.p.size());
    		f.p.resize(t);g.p.resize(t);
    		for(int i = 0 ; i < t ; ++i) {
    			h.p.pb(inc(f.p[i],MOD - g.p[i]));
    		}
    		for(int i = t - 1; i >= 0 ; --i) {
    			if(!h.p[i]) h.p.pop_back();
    			else break;
    		}
    		return h;
    	}
    	friend poly Inverse(poly f,int L) {
    		poly g,r,two;
    		two.p.pb(2);
    		g.p.pb(fpow(f.p[0],MOD - 2));r.p.pb(f.p[0]);
    		int t = 1;
    		while(t <= L) {
    			int m = min(t * 2,(int)f.p.size());
    			for(int i = t ; i < m ; ++i) {
    				r.p.pb(f.p[i]);
    			}
    			t = t * 2;
    			g = g * (two - r * g);
    			int tmp = g.p.size();
    			for(int i = tmp - 1; i >= t ; --i) g.p.pop_back();
    		}
    		t = g.p.size();
    		for(int i = t - 1 ; i >= L ; --i) g.p.pop_back();
    		t = L - 1;
    		for(int i = t ; i >= 0 ; --i) {
    			if(!g.p[i]) g.p.pop_back();
    			else break;
    		}
    		return g;
    	}
    	friend poly operator / (poly f,poly g) {
    		reverse(f.p.begin(),f.p.end());
    		reverse(g.p.begin(),g.p.end());
    		int t = f.p.size() - g.p.size() + 1;
    		poly h = Inverse(g,t);
    		poly q = f * h;
    		for(int i = q.p.size() - 1 ; i >= t ; --i) q.p.pop_back();
    		reverse(q.p.begin(),q.p.end());
    		for(int i = t - 1 ; i >= 0 ; --i) {
    			if(!q.p[i]) q.p.pop_back();
    			else break;
    		}
    
    		return q;
    	}
    	friend poly operator % (poly f,poly g) {
    		return f - g * (f / g);
    	}
    };
    poly tr[MAXN * 4];
    poly Solve1(int u,int l,int r) {
    	if(l == r) {
    		tr[u].p.pb(MOD - sum[l]);tr[u].p.pb(1);
    		return tr[u];
    	}
    	int mid = (l + r) >> 1;
    	tr[u << 1] = Solve1(u << 1,l,mid);
    	tr[u << 1 | 1] = Solve1(u << 1 | 1,mid + 1,r);
    	return tr[u] = tr[u << 1] * tr[u << 1 | 1];
    }
    void Solve2(int u,int l,int r,poly f) {
    	if(l == r) {
    		if(!f.p.size()) ans[l] = 0;
    		else ans[l] = f.p[0];
    		return;
    	}
    	int mid = (l + r) >> 1;
    	Solve2(u << 1,l,mid,f % tr[u << 1]);
    	Solve2(u << 1 | 1,mid + 1,r,f % tr[u << 1 | 1]);
    }
    void Init() {
    	W[0] = 1;
    	W[1] = fpow(3,(MOD - 1) / (1 << 20));
    	for(int i = 2 ; i < (1 << 20) ; ++i) {
    		W[i] = mul(W[i - 1],W[1]);
    	}
    	read(N);read(A);read(B);
    	int x,y;
    	for(int i = 1 ; i < N ; ++i) {
    		read(x);read(y);
    		add(x,y);add(y,x);
    	}
    	dfs(A);
    	int f = B,t = 0;
    	fac = 1;
    	while(t != A) {
    		sum[++tot] = siz[f] - siz[t];
    		fac = mul(fac,siz[f]);
    		t = f;
    		f = fa[f];
    	}
    	for(int i = 1 ; i <= tot ; ++i) sum[i] += sum[i - 1];
    	inv[1] = 1;
    	for(int i = 2 ; i <= N; ++i) {
    		inv[i] = mul(inv[MOD % i],MOD - MOD / i);
    		fac = mul(fac,i);
    	}
    	for(int i = 1 ; i <= N ; ++i) fac = mul(fac,inv[siz[i]]);
    	
    }
    void Solve() {
    	poly f = Solve1(1,0,tot);
    	int t = f.p.size();
    	for(int i = 0 ; i < t - 1 ; ++i) {
    		f.p[i] = mul(f.p[i + 1],i + 1);
    	}
    	f.p.pop_back();
    	
    	Solve2(1,0,tot,f);
    	int tmp = 1;
    	for(int i = tot ; i >= 0 ; --i) {
    		ans[i] = mul(ans[i],tmp);
    		tmp = mul(tmp,MOD - 1);
    	}
    	tmp = 0;
    	for(int i = 0 ; i <= tot ; ++i) {
    		tmp = inc(tmp,fpow(ans[i],MOD - 2));
    	}
    	fac = mul(fac,tmp);
    	fac = mul(fac,inv[2]);
    	out(fac);enter;
    }
    int main() {
    #ifdef ivorysi
    	freopen("f1.in","r",stdin);
    #endif
    	Init();
    	Solve();
    	return 0;
    }
    
  • 相关阅读:
    杂记
    实战:PSP上运行Windows 95
    [英语阅读笔记] CodeSnip: Uploading Multiple Files At Once
    整理网上的一些关于sharepoint编码方面的最佳实践 Virus
    ASP.NET、SharePoint中另存文件的长文件名被截断的原因及解决办法 Virus
    一个由Erlang引发的故事,关于语言和工资的故事 Virus
    ASP.NET统计图表控件 Virus
    关于验证码的思考 Virus
    Design Pattern 设计模式【观察者】 Virus
    moss开发实施过程中遇到的问题总结:项目层面 Virus
  • 原文地址:https://www.cnblogs.com/ivorysi/p/9410641.html
Copyright © 2020-2023  润新知