BZOJ 4719: [Noip2016]天天爱跑步 线段树合并
title
BZOJ 4719
LUOGU 1600
簡化題意:
小c同學認為跑步非常有趣,于是決定制作一款叫做《天天愛跑步》的游戲。《天天愛跑步》是一個養成類游戲,需要玩家每天按時上線,完成打卡任務。
這個游戲的地圖可以看作一一棵包含 \(n\) 個結點和 \(n-1\) 條邊的樹, 每條邊連接兩個結點,且任意兩個結點存在一條路徑互相可達。樹上結點編號為從 \(1\) 到 \(n\) 的連續正整數。
現在有 \(m\) 個玩家,第 \(i\) 個玩家的起點為 \(S_i\) ,終點為 \(T_i\) 。每天打卡任務開始時,所有玩家在第 \(0\) 秒同時從自己的起點出發,以每秒跑一條邊的速度,不間斷地沿著最短路徑向著自己的終點跑去,跑到終點后該玩家就算完成了打卡任務。 (由于地圖是一棵樹,所以每個人的路徑是唯一的)
小c想知道游戲的活躍度,所以在每個結點上都放置了一個觀察員。 在結點 \(j\) 的觀察員會選擇在第 \(W_j\) 秒觀察玩家,一個玩家能被這個觀察員觀察到當且僅當該玩家在第 \(W_j\) 秒也理到達了結點 \(j\) 。 小C想知道每個觀察員會觀察到多少人?
注意:我們認為一個玩家到達自己的終點后該玩家就會結束游戲,他不能等待一 段時間后再被觀察員觀察到。 即對于把結點 \(j\) 作為終點的玩家:若他在第 \(W_j\) 秒前到達終點,則在結點 \(j\) 的觀察員不能觀察到該玩家;若他正好在第 \(W_j\) 秒到達終點,則在結點 \(j\) 的觀察員可以觀察到這個玩家。
analysis
對于一個人,他的路程會分為兩段,一段向上(根),一段向下,考慮在向上過程中他能產生貢獻的觀察者具有什么性質:設出發點深度為 \(dep[x]\),觀察者深度為 \(dep[y]\),觀察的時間為 \(t\),需滿足 \(dep[x]?dep[y]=t\),換句話說就是 \(dep[y]+t=dep[x]\) 。向下那一段的推導類似,下面的部分也只以向上的路徑為例來解釋。
現在我們記錄每個點下方出發點深度為 \(dep[x]\) 的人數,需要對于每個出發點更新出發點到出發點與目的地的 \(lca\) 的所有點,想到到開一顆權值線段樹,用線段樹合并不斷把信息往上傳,在 \(lca\) 處消除這次更新的影響,這樣一來直接我們就可以在樹上統計答案了。向下路徑的處理與向上路徑類似。
code
#include<bits/stdc++.h>const int maxn=3e5+10;namespace IO {char buf[1<<15],*fs,*ft;inline char getc() { return (ft==fs&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),ft==fs))?0:*fs++; }template<typename T>inline void read(T &x){x=0;T f=1, ch=getchar();while (!isdigit(ch) && ch^'-') ch=getchar();if (ch=='-') f=-1, ch=getchar();while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();x*=f;}char Out[1<<24],*fe=Out;inline void flush() { fwrite(Out,1,fe-Out,stdout); fe=Out; }template<typename T>inline void write(T x,char str){if (!x) *fe++=48;if (x<0) *fe++='-', x=-x;T num=0, ch[20];while (x) ch[++num]=x%10+48, x/=10;while (num) *fe++=ch[num--];*fe++=str;} }using IO::read; using IO::write;int ver[maxn<<1],Next[maxn<<1],head[maxn],len; inline void add(int x,int y) {ver[++len]=y,Next[len]=head[x],head[x]=len; }namespace SGT {struct Orz{int l,r,z;}c[maxn*60];int num=0;inline void Change(int &x,int l,int r,int k,int z){if (!x) x=++num;c[x].z+=z;if (l==r) return ;int mid=(l+r)>>1;if (k<=mid) Change(c[x].l,l,mid,k,z);else Change(c[x].r,mid+1,r,k,z);}inline int query(int x,int l,int r,int k){if (l==r) return c[x].z;int mid=(l+r)>>1;if (k<=mid) return query(c[x].l,l,mid,k);else return query(c[x].r,mid+1,r,k);}inline int merge(int x,int y){if (!x) return y;if (!y) return x;c[x].l=merge(c[x].l,c[y].l);c[x].r=merge(c[x].r,c[y].r);c[x].z=c[x].z+c[y].z;return x;} }using SGT::Change; using SGT::query; using SGT::merge;namespace lca {int dfn[maxn],id;int f[maxn][21],dep[maxn];inline void dfs(int x){dfn[x]=++id;for (int i=1; i<=20; ++i) f[x][i]=f[f[x][i-1]][i-1];for (int i=head[x]; i; i=Next[i]){int y=ver[i];if (y==f[x][0]) continue;f[y][0]=x;dep[y]=dep[x]+1;dfs(y);}}inline int LCA(int x,int y){if (dep[x]>dep[y]) std::swap(x,y);for (int i=20; i>=0; --i)if (dep[y]-(1<<i)>=dep[x]) y=f[y][i];if (x==y) return x;for (int i=20; i>=0; --i)if (f[x][i]^f[y][i]) x=f[x][i],y=f[y][i];return f[x][0];} }using lca::dfs; using lca::LCA; using lca::dep; using lca::f;int tot,n,m,u[maxn],v[maxn]; int ans[maxn],w[maxn]; inline void get(int x) {for (int i=head[x]; i; i=Next[i]){int y=ver[i];if (y==f[x][0]) continue;get(y);u[x]=merge(u[x],u[y]), v[x]=merge(v[x],v[y]);}ans[x]=query(u[x],1,tot,dep[x]+w[x])+query(v[x],1,tot,w[x]-dep[x]+n); }int main() {read(n);read(m); tot=n<<1;for (int i=1,x,y; i<n; ++i) read(x),read(y),add(x,y),add(y,x);for (int i=1; i<=n; ++i) read(w[i]);dfs(1);for (int i=1,x,y; i<=m; ++i){read(x),read(y);int a=LCA(x,y);Change(u[x],1,tot,dep[x],1);Change(u[a],1,tot,dep[x],-1);Change(v[y],1,tot,dep[x]-(dep[a]<<1)+n,1);Change(v[f[a][0]],1,tot,dep[x]-(dep[a]<<1)+n,-1);}get(1);for (int i=1; i<=n; ++i) write(ans[i],' '); *IO::fe--;//防 BZOJIO::flush();return 0; }轉載于:https://www.cnblogs.com/G-hsm/p/11426646.html
總結
以上是生活随笔為你收集整理的BZOJ 4719: [Noip2016]天天爱跑步 线段树合并的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ios 触摸事件 学习
- 下一篇: 容联云通讯Demo