2023年考到一片oier的題目。利用樹上的差分來解決這個問題
。先求出兩個節點的lca,然後分成向上跑和向下跑兩個鏈。
向上從起點跑到lca,這個過程累加,過了lca,對統計的貢獻就沒有了,減掉。因此就結點來說統計乙個節點有多少人,就觀察點i來說,如果觀察點的值是wi,需要統計觀察點下方deep[i]+wi深度有幾個節點。
向下從根節點跑到lca。從節點左側d=deep[st]-deep[lca]深度跑來,相當於從deep[lca]-d這個方向上跑來。因此需要重新記錄這個結束點對應的起點深度。對其lca來說也是一樣的,也要記錄這個起點深度。在en點和lca點之間的跑步是有效的。因此en以上統計,lca以上不再統計,減去!
以上兩個在樹上的統計用dfs完成,統計每個節點 所有的孩子中,距離其深度為wi的人有幾個。
在這個過程中,如果恰好節點i的值wi=deep[st]-deep[i],那麼這個點在上行時候被統計一次,下行時候也被統計一次,先減去一次。再在其父節點減一次,這項就差分啦。
所以,題目的整體步驟應該先求深度、lca(倍增或tarjan),再根據上面的方法處理。
小細節:因為起點根據lca翻轉後的深度可能小於0,因此規定根節點的深度為3e5.
#include#include#include#includeconst int maxn=300009;
using namespace std;
struct node
}edge[2*maxn],ques[2*maxn];
struct node2
};int n,m,s,anslca[maxn],flca[maxn],st[maxn],ed[maxn],dep[maxn],fa[maxn],cnt=0,w[maxn];
int head[maxn],qhead[maxn];
bool vis[maxn];
int add(node e,int hd,int u,int v,int w);
hd[u]=cnt;
e[++cnt]=;
hd[v]=cnt;
}int find(int x)
void tarlca(int u)
}for(int i=qhead[u];i>0;i=ques[i].next)
}}vectorcup[maxn],cdown[maxn];
int aup[2*maxn],adown[2*maxn],ans[maxn];
void dfs(int u)
} ans[u]+=aup[dep[u]+w[u]];
ans[u]+=adown[dep[u]-w[u]];
}int main(){
memset(head,0,sizeof(head));
memset(qhead,0,sizeof(qhead));
int a,b;
scanf("%d%d",&n,&m);
for(int i=1;i
天天愛跑步NOIP
首先,我們考慮對於一條路徑 從x y,可以把它拆分成兩部分,圖中用虛線分開,然後這條路徑就變成了x lca,son lca y 先來考慮從x向上走到lca的路徑,對於這條路上的節點i,玩家能對節點i產生貢獻的前提是deep x w i deep i 移項可得deep x w i deep i 也就是...
NOIP2016 天天愛跑步
時間限制 2 s 記憶體限制 512 mb 題目描述 小c同學認為跑步非常有趣,於是決定製作一款叫做 天天愛跑步 的遊戲。天天愛跑步 是乙個養成類遊戲,需要玩家每天按時上線,完成打卡任務。這個遊戲的地圖可以看作一棵包含n個結點和n 1條邊的樹,每條邊連線兩個結點,且任意兩個結點存在一條路徑互相可達。...
NOIP2016天天愛跑步
小c同學認為跑步非常有趣,於是決定製作一款叫做 天天愛跑步 的遊戲。天天愛跑步 是乙個養成類遊戲,需要玩家每天按時上線,完成打卡任務。這個遊戲的地圖可以看作一一棵包含 nn n個結點和 n 1n 1n 1條邊的樹,每條邊連線兩個結點,且任意兩個結點存在一條路徑互相可達。樹上結點編號為從11 1到nn...