• [省选联考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;
    }
    
  • 相关阅读:
    Codeforces-754D Fedor and coupons
    LightOJ
    LightOJ
    LightOJ
    LightOJ
    POJ
    HDU
    HDU
    HDU-2159
    方法的重写
  • 原文地址:https://www.cnblogs.com/dixiao/p/16161892.html
Copyright © 2020-2023  润新知