NOIP2016 天天愛跑步

2022-03-29 21:27:30 字數 1666 閱讀 6147

看這道題不爽很久了,但一直沒有開它,原因是我不會(我太菜了),看了題解還是寫不來,因為我不會線段樹合併。

然後今天學了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條邊的樹,每條邊連線兩個結點,且任意兩個結點存在一條路徑互相可達。樹上結點編...