BZOJ3451 Normal 期望、点分治、NTT
生活随笔
收集整理的這篇文章主要介紹了
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; }轉載于:https://www.cnblogs.com/Itst/p/10268440.html
總結
以上是生活随笔為你收集整理的BZOJ3451 Normal 期望、点分治、NTT的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 烂代码解决方案:Java设计模式的魅力
- 下一篇: 微信小程序支付最容易犯的坑notify_