看這道題不爽很久了,但一直沒有開它,原因是我不會(我太菜了),看了題解還是寫不來,因為我不會線段樹合併。
然後今天學了dsu on tree這種神奇的科技,成功把它a了,效率吊打線段樹合併。
於是寫篇題解紀念一下。
洛谷p1600 天天愛跑步
不帶修改的樹上路徑資訊的維護,很容易想到樹上差分。
我們考慮一條路徑,會對哪些點產生貢獻,也就是題目描述中的乙個人會被哪些觀察員觀察到。
假設這條路徑為 \((s,t)\),點為 \(x\),我們分兩部分考慮:
我們設第一種情況開的桶為 \(up\),第二種情況開的桶為 \(down\),那麼對於每個點 \(x\),其最終答案即為 \(up[w[x]+dep[x]]+down[w[x]-dep[x]]\)。
但問題是,我們在每個點上開兩個桶是顯然不可能的,而且就算空間能夠支援每個點開兩個桶,但我們在統計過程中,合併兩個桶的時間複雜度也直接**。
所以我們只能在全域性開兩個桶。
那麼怎麼維護子樹資訊呢?
不帶修改的子樹資訊維護,正是dsu on tree大顯身手的領域。
所以在每個點上開個 \(vector\),把每個點要維護的資訊放進去,然後dsu on tree,需要統計資訊的時候再把資訊拿出來統計就好了。
每個點維護資訊的時間均攤是 \(\frac\) 的,每個點最多會被統計 \(o(logn)\) 次,一共有 \(n\) 個點,所以最後的時間複雜度是 \(o(mlogn)\) 的。
#include #include #include #include #include using namespace std;
const int n=3e5+10;
struct edgeedge[n<<1];int idx;
int h[n];
void add_edge(int u,int v);h[u]=idx;}
int fa[n],dep[n],siz[n];
int son[n],top[n],w[n];
vectorinfo[n][4];
int up[n<<1],buf[n<<2],*down=&buf[n<<1];
int ans[n];
int n,m;
void dfs1(int p,int f)
}void dfs2(int p,int t)
}inline int lca(int x,int y)
return dep[x]}void calc(int p,int flag,int val)
}void dfs(int p,int keep)
if(son[p])dfs(son[p],1);
calc(p,son[p],1);
ans[p]=up[dep[p]+w[p]]+down[w[p]-dep[p]];
if(!keep)calc(p,0,-1);
}int main()
for(int i=1;i<=n;i++)scanf("%d",&w[i]);
dfs1(1,0);
dfs2(1,1);
for(int i=1,s,t;i<=m;i++)
dfs(1,1);
for(int i=1;i<=n;i++)printf("%d ",ans[i]);
return 0;
}
NOIP2016 天天愛跑步
時間限制 2 s 記憶體限制 512 mb 題目描述 小c同學認為跑步非常有趣,於是決定製作一款叫做 天天愛跑步 的遊戲。天天愛跑步 是乙個養成類遊戲,需要玩家每天按時上線,完成打卡任務。這個遊戲的地圖可以看作一棵包含n個結點和n 1條邊的樹,每條邊連線兩個結點,且任意兩個結點存在一條路徑互相可達。...
NOIP2016天天愛跑步
小c同學認為跑步非常有趣,於是決定製作一款叫做 天天愛跑步 的遊戲。天天愛跑步 是乙個養成類遊戲,需要玩家每天按時上線,完成打卡任務。這個遊戲的地圖可以看作一一棵包含 nn n個結點和 n 1n 1n 1條邊的樹,每條邊連線兩個結點,且任意兩個結點存在一條路徑互相可達。樹上結點編號為從11 1到nn...
NOIP2016天天愛跑步
時間限制 2 s 記憶體限制 512 mb 小c同學認為跑步非常有趣,於是決定製作一款叫做 天天愛跑步 的遊戲。天天愛跑步 是乙個養成類遊戲,需要玩家每天按時上線,完成打卡任務。這個遊戲的地圖可以看作一棵包含n個結點和n 1條邊的樹,每條邊連線兩個結點,且任意兩個結點存在一條路徑互相可達。樹上結點編...