[省選聯考2022 ] 填樹
阿新 • • 發佈:2022-04-18
今天在考 \(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;
}