[省选联考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,R] - [L + 1,R]\)

那么考虑在全值域上枚举 \(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;
}
posted @ 2022-04-18 20:01  fhq_treap  阅读(309)  评论(0编辑  收藏  举报