[省选联考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;
}