NOI 2014-2015 題目選做
阿新 • • 發佈:2022-05-09
說句閒話,為什麼我把近年的 noi
基本都刷完了,還是感受不到它的出題風格?
購票
題目描述
解法
設 \(dp[u]\) 表示從 \(u\) 走到根的最小花費,那麼很容易寫出轉移:
\[dp[u]\leftarrow dp[x]+(d_u-d_x)\cdot p_u+q_u\ ,\ d_u-d_x\leq l_u \]如果不考慮距離的限制,可以直接用可持久化李超樹解決,結合暴力可以獲得 \(70\) 分的高分!
發現考慮上距離的限制其實就是多加了一維偏序關係,所以我們可以用樹套樹。一種空間消耗較小的樹套樹方法是,我們預處理出每個點的歐拉出序,外層線段樹以歐拉出序來建立。
詢問時先二分出可以到達的最淺祖先,然後可以得到一個歐拉出序的區間,這個區間只包含這個點到祖先的路徑,因為還沒訪問到的點沒被加入到李超樹中,那麼插入的時候不用回撤,直接插入即可。
由於使用的是全域性李超樹,並且配合動態開點,時間複雜度 \(O(n\log^2 n)\),空間複雜度 \(O(n\log n)\)
#include <cstdio> #include <vector> #include <iostream> using namespace std; const int M = 200005; #define int long long const int inf = 2e18; int read() { int x=0,f=1;char c; while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;} while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();} return x*f; } int n,m,o[M],a[M],c[M],d[M],p[M],q[M],l[M],dp[M]; int cnt,rt[M<<2],ls[M*20],rs[M*20];vector<int> g[M]; struct node { int k,b; node(int K=0,int B=inf) : k(K) , b(B) {} int get(int x) {return x*k+b;} }s[M*20]; void ins(int &x,int l,int r,node t) { if(!x) {s[x=++cnt]=t;return ;} int mid=(l+r)>>1; if(t.get(mid)<s[x].get(mid)) swap(t,s[x]); if(l==r) return ; if(t.get(l)<s[x].get(l)) ins(ls[x],l,mid,t); if(t.get(r)<s[x].get(r)) ins(rs[x],mid+1,r,t); } int qry(int x,int l,int r,int p) { int t=s[x].get(p),mid=(l+r)>>1; if(!x || l==r) return t; if(p<=mid) return min(t,qry(ls[x],l,mid,p)); return min(t,qry(rs[x],mid+1,r,p)); } void upd(int i,int l,int r,int x,node t) { ins(rt[i],0,1e6,t); if(l==r) return ; int mid=(l+r)>>1; if(mid>=x) upd(i<<1,l,mid,x,t); else upd(i<<1|1,mid+1,r,x,t); } int ask(int i,int l,int r,int L,int R,int x) { if(L>r || l>R) return inf; if(L<=l && r<=R) return qry(rt[i],0,1e6,x); int mid=(l+r)>>1; return min(ask(i<<1,l,mid,L,R,x), ask(i<<1|1,mid+1,r,L,R,x)); } void pre(int u) { for(int &v:g[u]) pre(v); o[u]=++m; } void dfs(int u) { c[m]=u;upd(1,1,n,o[u],node(-a[m],dp[u])); for(int v:g[u]) { m++;a[m]=a[m-1]+d[v]; int x=o[c[lower_bound(a,a+m,a[m]-l[v])-a]]; dp[v]=ask(1,1,n,o[u],x,p[v])+a[m]*p[v]+q[v]; dfs(v);m--; } } signed main() { n=read();read(); for(int i=2;i<=n;i++) { g[read()].push_back(i);d[i]=read(); p[i]=read();q[i]=read();l[i]=read(); } pre(1);m=0;dfs(1); for(int i=2;i<=n;i++) printf("%lld\n",dp[i]); }