1. 程式人生 > 其它 >[省選聯考2022 ] 填樹

[省選聯考2022 ] 填樹

今天在考 \(DAY1\)
大概 \(100 + 100 + [20,40] = [220,240]\)

因為 \(T1\) 是有細節的模擬題,\(T3\) 是暴力,所以略去。

先考慮 \(O(nm)\) 的做法。

考慮列舉路徑上的最小值:
然後計
\(f_{x,0/1}\) 為以 \(x\) 為根的鏈,其中值域全在列舉的 \([L,L + k]\) 中,其中否/是有 \(L\) 的方案數。
\(g_{x,0/1}\) 為以 \(x\) 為根的鏈,其中值域全在列舉的 \([L,L + k]\) 中,其中否/是有 \(L\) 的權值和。

那麼可以寫出轉移式:

inline void dfs(int u,int fa){
	int li = std::max(L,l[u]),ri = std::min(R,r[u]);
	ll now,res;
	if(li > ri)now = 0,res = 0;else now = (li <= L),res = (ri - li + 1) - now;
	f[u][1] = now,f[u][0] = res % mod;
	ll gnow,gres;
	gnow = (now ? L : 0),gres = len(li,ri) - (now ? L : 0);
	g[u][1] = gnow,g[u][0] = gres;
	ans = (ans + f[u][1]) % mod; 
	gans = (gans + gnow) % mod;
	for(auto v : T[u]){
		if(v == fa)continue;
		dfs(v,u); 
		ans = (ans + (f[u][1]) * ((f[v][0] + f[v][1]) % mod) % mod + f[u][0] * f[v][1] % mod) % mod;
		gans = (gans + (f[u][1]) * (g[v][0] + g[v][1]) % mod + (g[u][1]) * (f[v][0] + f[v][1]) % mod + (g[u][0]) * f[v][1] % mod + f[u][0] * (g[v][1]) % mod) % mod;		
//		std::cout<<u<<" -> "<<v<<" "<<((f[u][1]) * (g[v][0] + g[v][1]) % mod + (g[u][1]) * (f[v][0] + f[v][1]) % mod + (g[u][0]) * f[v][1] % mod + f[u][0] * (g[v][1]) % mod)<<"\n";		
		f[u][1] = (f[u][1] + now * (f[v][0] + f[v][1]) % mod + res * f[v][1] % mod) % mod;
		f[u][0] = (f[u][0] + res * f[v][0] % mod) % mod;
		g[u][1] = (g[u][1] + now * (g[v][0] + g[v][1]) % mod + gnow * (f[v][0] + f[v][1]) % mod + gres * f[v][1] % mod + res * g[v][1]) % mod;
		g[u][0] = (g[u][0] + res * g[v][0] % mod + gres * f[v][0]) % mod;
	}  
//	std::cout<<u<<" "<<li<<" "<<ri<<" "<<g[u][1]<<" "<<g[u][0]<<" "<<f[u][1]<<" "<<f[u][0]<<"\n";
}

這樣就可以做到 \(O(nm)\)

接下來我們論證:

\(F_x,G_x\) 為列舉最小值為 \(x\) 的方案數/權值和
我們下列證明 \(F_x,G_x\) 均為分段函式,每一段均為一個多項式:

考慮歸納證明,對於每個葉子選擇 \([l,r]\) :其的 \(f\) 為一個一次函式 \((r - l + 1)\)\(g\) 為一個二次函式 (\(\frac{(l + r)(r - l + 1)}{2}\)),每次加減乘除均不影響其答案為多項式
那麼考慮在全值域上列舉 \(L\),當對答案多項式實際上有影響時:只有以下四種情況:
一個點的 \(l_i <= L + K\)

:即多了一個點加入答案
一個點的 \(l_i <= L\) : 即多了一個全集點
一個點的 \(r_i < L\) :即少了一個點加入答案
一個點的 \(r_i < L + k\) : 即少了一個全集點
那麼我們完全可以對其分段操作:
按照 \(l_i,l_i - K,r_i + 1,r_i - K + 1\) 作為分界點,排序後設為 \(c_i\)

對每段 \([c_i,c_{i + 1})\),跑出前 \(n + 2\) 個點的答案,然後求其求字首和,插值出 \(pf_{c_{i + 1} - 1},pg_{c_{i+ 1}-1}\)

建議這種給定值值域連續的使用線性插法(在上篇機器人裡有說)

分析一下複雜度:
複雜度瓶頸在求出每一段的前 \(n + 2\) 個點,\(O(n^3)\)

點選檢視程式碼
//晦暗的宇宙,我們找不到光,看不見盡頭,但我們永遠都不會被黑色打倒。——Quinn葵因
#include<bits/stdc++.h>
#define ll long long
#define N 400
#define mod (ll)(1e9 + 7)

ll f[N][2];//first / got top : 1 / 0 
ll g[N][2];//second / got top : 1 / 0 

int n,K;

int l[N],r[N];

using std::vector;

vector<int>T[N];

int m;

int L,R;

ll ans,gans;

inline ll len(ll l,ll r){
	if(r < l)return 0;
	return (l + r) * (r - l + 1) / 2 % mod;
}

inline void dfs(int u,int fa){
	int li = std::max(L,l[u]),ri = std::min(R,r[u]);
	ll now,res;
	if(li > ri)now = 0,res = 0;else now = (li <= L),res = (ri - li + 1) - now;
	f[u][1] = now,f[u][0] = res % mod;
	ll gnow,gres;
	gnow = (now ? L : 0),gres = len(li,ri) - (now ? L : 0);
	g[u][1] = gnow,g[u][0] = gres;
	ans = (ans + f[u][1]) % mod; 
	gans = (gans + gnow) % mod;
	for(auto v : T[u]){
		if(v == fa)continue;
		dfs(v,u); 
		ans = (ans + (f[u][1]) * ((f[v][0] + f[v][1]) % mod) % mod + f[u][0] * f[v][1] % mod) % mod;
		gans = (gans + (f[u][1]) * (g[v][0] + g[v][1]) % mod + (g[u][1]) * (f[v][0] + f[v][1]) % mod + (g[u][0]) * f[v][1] % mod + f[u][0] * (g[v][1]) % mod) % mod;		
//		std::cout<<u<<" -> "<<v<<" "<<((f[u][1]) * (g[v][0] + g[v][1]) % mod + (g[u][1]) * (f[v][0] + f[v][1]) % mod + (g[u][0]) * f[v][1] % mod + f[u][0] * (g[v][1]) % mod)<<"\n";		
		f[u][1] = (f[u][1] + now * (f[v][0] + f[v][1]) % mod + res * f[v][1] % mod) % mod;
		f[u][0] = (f[u][0] + res * f[v][0] % mod) % mod;
		g[u][1] = (g[u][1] + now * (g[v][0] + g[v][1]) % mod + gnow * (f[v][0] + f[v][1]) % mod + gres * f[v][1] % mod + res * g[v][1]) % mod;
		g[u][0] = (g[u][0] + res * g[v][0] % mod + gres * f[v][0]) % mod;
	}  
//	std::cout<<u<<" "<<li<<" "<<ri<<" "<<g[u][1]<<" "<<g[u][0]<<" "<<f[u][1]<<" "<<f[u][0]<<"\n";
}

int st[N * 10];
int cnt;

ll y[N],q[N];//點值 

ll fans,tans;

ll S[N],INV[N];//階乘  階乘逆元 
ll suf[N];

inline ll lange(ll x){
//	std::cout<<"O WHAT HAPPEN"<<"\n";
//	for(int i = 1;i <= n + 2;++i){
//		std::cout<<y[i]<<" "; 
//	}
//	puts("");
	ll res = 0;
	suf[n + 3] = 1;
	for(int i = n + 2;i >= 1;--i)
	suf[i] = suf[i + 1] * (x - i) % mod; 
	ll pre = 1; 
	for(int i = 1;i <= n + 2;++i){
		res = (res + ((n + 2 - i) & 1 ? mod - INV[n + 2 - i] : INV[n + 2 - i]) * y[i] % mod * pre % mod * suf[i + 1] % mod * INV[i - 1] % mod) % mod;
		pre = pre * (x - i) % mod;
	}
//	std::cout<<res<<"\n";
	return res;
}

inline ll qpow(ll a,ll b){
	ll res = 1;
	while(b){
		if(b & 1)res = res * a % mod;
		b >>= 1;
		a = a * a % mod;
	} 
	return res;
}

inline void init(){
	scanf("%d%d",&n,&K);
	for(int i = 1;i <= n;++i)
	scanf("%d%d",&l[i],&r[i]),m = std::max(m,r[i]);
	for(int i = 1;i < n;++i){
		int u,v;
		scanf("%d%d",&u,&v);
		T[u].push_back(v);
		T[v].push_back(u);
	}
	for(int i = 1;i <= n;++i){
		st[++cnt] = l[i],st[++cnt] = r[i] + 1;
		st[++cnt] = l[i] - K;
		st[++cnt] = r[i] - K + 1;				
	}
	std::sort(st + 1,st + cnt + 1);
	cnt = std::unique(st + 1,st + cnt + 1) - st - 1;		
	S[0] = 1;
//	puts("S"); 
	for(int i = 1;i <= n + 2;++i)
	S[i] = S[i - 1] * (i) % mod;/*std::cout<<S[i]<<" ";*/
	/*puts("");*/ 
	for(int i = 0;i <= n + 2;++i)
	INV[i] = qpow(S[i],mod - 2);/*std::cout<<INV[i]<<" "<<INV[i] * S[i] % mod<<"\n";*/ 
}

inline void solve(){
//	for(int i = 1;i <= cnt;++i)
//	std::cout<<st[i]<<" ";	
//	puts("");
	for(int i = 1;i <= cnt - 1;++i){
		if(st[i] <= 0)continue;
//		std::cout<<"FUCK "<<st[i]<<" "<<st[i + 1] - 1<<"\n";
		for(int i = 1;i <= n + 2;++i)y[i] = q[i] = 0;
		for(int p = st[i];p <= std::min(st[i] + n + 2,st[i + 1] - 1);++p){
			ans = 0,gans = 0;
			L = p,R = p + K; 
			dfs(1,0);
			y[p - st[i] + 1] = ans;
			q[p - st[i] + 1] = gans;
		}
		for(int p = 1;p <= n + 2;++p)
		y[p] = (y[p] + y[p - 1]) % mod,q[p] = (q[p] + q[p - 1]) % mod;
		if(st[i + 1] - st[i] <= n + 2)
		fans = (fans + y[n + 2]) % mod,tans = (tans + q[n + 2]);/*,std::cout<<y[n + 2]<<"\n";*/
		else {
		fans = (fans + lange(st[i + 1] - st[i])) % mod;/*,std::cout<<lange(st[i + 1] - st[i])<<"\n";*/
		for(int p = 1;p <= n + 2;++p)
		y[p] = q[p];
		tans = (tans + lange(st[i + 1] - st[i])) % mod;/*,std::cout<<lange(st[i + 1] - st[i])<<"\n";*/			
		}		
	}
	L = st[cnt],R = st[cnt] + K;
	ans = 0,gans = 0;
	dfs(1,0);
//	std::cout<<ans<<"\n";
	fans = (fans + ans) % mod;
	tans = (tans + gans) % mod;
}

int main(){
	freopen("tree.in","r",stdin);
	freopen("tree.out","w",stdout);
	init();
	solve();
	std::cout<<fans<<"\n"<<tans;
}