• BZOJ3451 Normal 期望、点分治、NTT


    BZOJCH传送门

    题目大意:给出一棵树,求对其进行随机点分治的复杂度期望


    可以知道一个点的贡献就是其点分树上的深度,也就是这个点在点分树上的祖先数量+1。

    根据期望的线性性,考虑一个点对((x,y))在何时(x)能够是(y)的祖先,那么在(x)(y)的路径上的所有点中(x)必须要是第一个被选中的点,否则要么点(y)变成点(x)的祖先,要么点(x)和点(y)会被分在不同的子树中。那么我们要求的就是(sum frac{1}{dis_{x,y}}),其中(dis_{x,y})表示(x)(y)路径上的点的数量。直接使用(NTT)统计路径长度即可。

    注意在某一个分治中心进行合并时必须按照深度从小到大合并,否则复杂度是不正确的。

    #include<bits/stdc++.h>
    #define INF 0x7fffffff
    using namespace std;
    
    inline int read(){
    	int a = 0;
    	char c = getchar();
    	while(!isdigit(c))
    		c = getchar();
    	while(isdigit(c)){
    		a = a * 10 + c - 48;
    		c = getchar();
    	}
    	return a;
    }
    
    const int MAXN = 1e5 + 10 , MOD = 998244353 , G = 3 , INV = 332748118;
    struct Edge{
    	int end , upEd;
    }Ed[MAXN << 1];
    int head[MAXN] , ind[MAXN] , size[MAXN] , ans[MAXN];
    int N , need , nSize , mSize , mInd , cntEd;
    bool vis[MAXN];
    long long inv_need;
    vector < vector < int > > v;
    
    inline void addEd(int a , int b){
    	Ed[++cntEd].end = b;
    	Ed[cntEd].upEd = head[a];
    	head[a] = cntEd;
    }
    
    inline int poww(long long a , int b){
    	int times = 1;
    	while(b){
    		if(b & 1)
    			times = times * a % MOD;
    		a = a * a % MOD;
    		b >>= 1;
    	}
    	return times;
    }
    
    void NTT(vector < int > &v , int type){
    	for(int i = 0 ; i < need ; ++i)
    		if(i < ind[i])
    			swap(v[i] , v[ind[i]]);
    	for(int i = 1 ; i < need ; i <<= 1){
    		int wn = poww(type == 1 ? G : INV , (MOD - 1) / (i << 1));
    		for(int j = 0 ; j < need ; j += i << 1){
    			long long w = 1;
    			for(int k = 0 ; k < i ; ++k , w = w * wn % MOD){
    				int x = v[j + k] , y = v[i + j + k] * w % MOD;
    				v[j + k] = x + y >= MOD ? x + y - MOD : x + y;
    				v[i + j + k] = x < y ? x - y + MOD : x - y;
    			}
    		}
    	}
    }
    
    void getSize(int x){
    	vis[x] = 1;
    	++nSize;
    	for(int i = head[x] ; i ; i = Ed[i].upEd)
    		if(!vis[Ed[i].end])
    			getSize(Ed[i].end);
    	vis[x] = 0;
    }
    
    void getRoot(int x){
    	vis[x] = 1;
    	int maxN = 0;
    	size[x] = 1;
    	for(int i = head[x] ; i ; i = Ed[i].upEd)
    		if(!vis[Ed[i].end]){
    			getRoot(Ed[i].end);
    			maxN = max(maxN , size[Ed[i].end]);
    			size[x] += size[Ed[i].end];
    		}
    	maxN = max(maxN , nSize - size[x]);
    	if(maxN < mSize){
    		mSize = maxN;
    		mInd = x;
    	}
    	vis[x] = 0;
    }
    
    void calc(vector < int > &v , int x , int l){
    	while(v.size() <= l)
    		v.push_back(0);
    	++v[l];
    	vis[x] = 1;
    	for(int i = head[x] ; i ; i = Ed[i].upEd)
    		if(!vis[Ed[i].end])
    			calc(v , Ed[i].end , l + 1);
    	vis[x] = 0;
    }
    
    bool cmp(vector < int > a , vector < int > b){
    	return a.size() < b.size();
    }
    
    void solve(int x){
    	nSize = 0;
    	mSize = INF;
    	getSize(x);
    	getRoot(x);
    	x = mInd;
    	vis[x] = 1;
    	v.clear();
    	vector < int > cur , ano;
    	for(int i = head[x] ; i ; i = Ed[i].upEd)
    		if(!vis[Ed[i].end]){
    			cur.clear();
    			calc(cur , Ed[i].end , 1);
    			v.push_back(cur);
    		}
    	cur.clear();
    	sort(v.begin() , v.end());
    	need = 1;
    	for(int i = 0 ; i < v.size() ; ++i){
    		while(need < cur.size() + v[i].size())
    			need <<= 1;
    		inv_need = poww(need , MOD - 2);
    		for(int j = 1 ; j < need ; ++j)
    			ind[j] = (ind[j >> 1] >> 1) | (j & 1 ? need >> 1 : 0);
    		while(cur.size() < need)
    			cur.push_back(0);
    		while(v[i].size() < need)
    			v[i].push_back(0);
    		ano.clear();
    		NTT(cur , 1);
    		NTT(v[i] , 1);
    		for(int j = 0 ; j < need ; ++j){
    			ano.push_back(1ll * cur[j] * v[i][j] % MOD);
    			cur[j] = cur[j] + v[i][j] >= MOD ? cur[j] + v[i][j] - MOD : cur[j] + v[i][j];
    		}
    		NTT(ano , -1);
    		NTT(cur , -1);
    		for(int j = 0 ; j < need ; ++j){
    			ans[j] += ano[j] * inv_need % MOD;
    			cur[j] = cur[j] * inv_need % MOD;
    		}
    		while(cur.back() == 0)
    			cur.pop_back();
    	}
    	for(int i = 1 ; i < need ; ++i)
    		ans[i] += cur[i];
    	for(int i = head[x] ; i ; i = Ed[i].upEd)
    		if(!vis[Ed[i].end])
    			solve(Ed[i].end);
    }
    
    int main(){
    	N = read();
    	for(int i = 1 ; i < N ; ++i){
    		int a = read() , b = read();
    		addEd(a , b);
    		addEd(b , a);
    	}
    	solve(1);
    	long double sum = 0;
    	for(int i = 1 ; i <= N ; ++i)
    		sum += ans[i] * 2.0 / (i + 1);
    	cout << fixed << setprecision(4) << sum + N;
    	return 0;
    }
    
  • 相关阅读:
    [公告]Google个性化主页可以正常阅读博客园的RSS了
    致歉
    [公告]网站程序已经升级到ASP.NET 2.0
    GTF: Great Teacher Friedman
    Node.js : exports と module.exports の違い
    拨开历史的迷雾从篡夺者战争到五王之战的政经原因
    javascript模板系统 ejs v10
    window.name + postMessage实现不用代理页的跨域通信
    node.js Domain 時代のエラー処理のコーディングパターン
    鲜为人知的get,set操作符
  • 原文地址:https://www.cnblogs.com/Itst/p/10268440.html
Copyright © 2020-2023  润新知