Luogu P1600 天天愛跑步
阿新 • • 發佈:2020-11-04
昨天的原題大戰考到了這題,發現我之前竟然沒做過就順便水篇部落格
首先對於一條路徑\(x\to y\),我們顯然可以根據它們的\(\operatorname{LCA}\)把路徑分成兩段
- 對於路徑\(x\to z\),我們發現上面的所有點滿足時間與深度之和不變
- 對於路徑\(z\to y\),我們發現上面的所有點滿足時間與深度之差不變
那麼很自然我們想到分別維護兩種路徑,不過要注意\(z\)不要算重了
現在就以第一種和的路徑為例,我們發現樹上路徑加增量的操作顯然可以直接差分
由於差分之後需要求的是子樹和,我們很容易想到把時間與深度之和作為下標建線段樹,每次上傳直接線段樹合併即可
總複雜度\(O(n\log n)\)
#include<cstdio> #include<iostream> #define RI register int #define CI const int& using namespace std; const int N=300005,SZ=1e7,P=20; struct edge { int to,nxt; }e[N<<1]; int n,m,head[N],cnt,x,y,w[N],dep[N],anc[N][P],ans[N],rt1[N],rt2[N]; inline void addedge(CI x,CI y) { e[++cnt]=(edge){y,head[x]}; head[x]=cnt; e[++cnt]=(edge){x,head[y]}; head[y]=cnt; } class Segment_Tree { private: struct segment { int ch[2],sum; }node[SZ]; int tot; public: #define lc(x) node[x].ch[0] #define rc(x) node[x].ch[1] #define S(x) node[x].sum #define TN CI l=0,CI r=3*n inline void insert(int& now,CI pos,CI mv,TN) { if (!now) now=++tot; if (l==r) return (void)(S(now)+=mv); int mid=l+r>>1; if (pos<=mid) insert(lc(now),pos,mv,l,mid); else insert(rc(now),pos,mv,mid+1,r); } inline void merge(int& x,CI y,TN) { if (!y) return; if (!x) return (void)(x=y); S(x)+=S(y); int mid=l+r>>1; merge(lc(x),lc(y),l,mid); merge(rc(x),rc(y),mid+1,r); } inline int query(CI now,CI pos,TN) { if (!now) return 0; if (l==r) return S(now); int mid=l+r>>1; if (pos<=mid) return query(lc(now),pos,l,mid); else return query(rc(now),pos,mid+1,r); } #undef lc #undef rc #undef S #undef TN }SEG1,SEG2; #define to e[i].to class Double_Increased { public: inline int getlca(int x,int y) { if (dep[x]<dep[y]) swap(x,y); RI i; for (i=P-1;~i;--i) if (dep[anc[x][i]]>=dep[y]) x=anc[x][i]; if (x==y) return x; for (i=P-1;~i;--i) if (anc[x][i]!=anc[y][i]) x=anc[x][i],y=anc[y][i]; return anc[x][0]; } inline void DFS1(CI now=1,CI fa=0) { dep[now]=dep[anc[now][0]=fa]+1; RI i; for (i=0;i<P-1;++i) if (anc[now][i]) anc[now][i+1]=anc[anc[now][i]][i]; else break; for (i=head[now];i;i=e[i].nxt) if (to!=fa) DFS1(to,now); } inline void DFS2(CI now=1,CI fa=0) { for (RI i=head[now];i;i=e[i].nxt) if (to!=fa) DFS2(to,now),SEG1.merge(rt1[now],rt1[to]),SEG2.merge(rt2[now],rt2[to]); ans[now]=SEG1.query(rt1[now],n+dep[now]+w[now])+SEG2.query(rt2[now],n+dep[now]-w[now]); } }T; #undef to int main() { //freopen("race.in","r",stdin); freopen("race.out","w",stdout); RI i; for (scanf("%d%d",&n,&m),i=1;i<n;++i) scanf("%d%d",&x,&y),addedge(x,y); for (i=1;i<=n;++i) scanf("%d",&w[i]); for (T.DFS1(),i=1;i<=m;++i) { scanf("%d%d",&x,&y); int z=T.getlca(x,y); SEG1.insert(rt1[x],n+dep[x],1); if (z!=1) SEG1.insert(rt1[anc[z][0]],n+dep[x],-1); SEG2.insert(rt2[z],n+dep[z]-(dep[x]-dep[z]),-1); SEG2.insert(rt2[y],n+dep[z]-(dep[x]-dep[z]),1); } for (T.DFS2(),i=1;i<=n;++i) printf("%d ",ans[i]); return 0; }