1. 程式人生 > >【NOIP2016提高組T2】天天愛跑步-倍增LCA+樹上差分

【NOIP2016提高組T2】天天愛跑步-倍增LCA+樹上差分

測試地址:天天愛跑步

做法:這裡轉載一下我看的題解:點這裡,這裡面對於整個題的做法應該寫的很明確了,這裡就不再贅述了。我的做法是實時用倍增求出路徑兩點的LCA(當然也可以離線用Tarjan做,貌似快一點),然後用類似鄰接表的連結串列結構儲存上面題解裡面的“人”,結構體裡有四個引數:t,d,p,next,t就是指出發的時間,d是指這個人是一個+1的人還是一個-1的人(值為1或-1),p是指這個人是一個從當前點走向根的人(為0)還是一個從根走向當前點的人(為1),next就指向與當前點有聯絡的下一個人,注意每一條路徑都會產生這樣的4個人,陣列要開夠。然後注意如果對於每個節點都開一個桶來儲存以該結點為根的子樹上各權值的人數的話,給你幾十G的記憶體都沒法做,這時就只用開兩個桶,一個桶儲存目前為止走向根的人中各權值的人數,另一個桶儲存目前為止從根出發的人中各權值的人數,然後在DFS到一個節點時,先把與這個點關聯的兩個權值上的人數記錄下來,在對其子樹進行DFS完後再用桶中的值減掉這兩個值,這樣就可以保證留下來的資訊都是子樹上的資訊。最後注意算出來的下標可能為負數,把所有下標加個n變成非負數即可。

以下是本人程式碼:

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
int n,m,w[300010],dep[300010]={0},first[300010]={0},firstq[300010]={0},tot=0;
int fa[300010][20]={0},bucket1[1000010]={0},bucket2[1000010]={0},ans[300010]={0};
bool vis[300010]={0};
struct edge {int v,next;} e[600010];
struct path {int t,d,next;bool p;} q[1200010];

void insert(int a,int b)
{
  e[++tot].v=b,e[tot].next=first[a],first[a]=tot;
}

void insertq(int a,int t,int d,bool p)
{
  q[++tot].t=t,q[tot].d=d,q[tot].p=p,q[tot].next=firstq[a],firstq[a]=tot;
}

void dfs(int v)
{
  vis[v]=1;
  for(int i=first[v];i;i=e[i].next)
    if (!vis[e[i].v])
	{
	  dep[e[i].v]=dep[v]+1;
	  fa[e[i].v][0]=v;
	  dfs(e[i].v);
	}
}

int lca(int x,int y)
{
  if (dep[x]<dep[y]) swap(x,y);
  for(int i=19;i>=0;i--)
    if (dep[fa[x][i]]>=dep[y]) x=fa[x][i];
  if (x==y) return x;
  for(int i=19;i>=0;i--)
    if (fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
  return fa[x][0];
}

void find_ans(int v)
{
  int x1,val1,x2,val2;
  x1=w[v]+dep[v]+n,x2=w[v]-dep[v]+n;
  val1=bucket1[x1],val2=bucket2[x2];
  for(int i=firstq[v];i;i=q[i].next)
  {
    if (!q[i].p) bucket1[q[i].t+dep[v]+n]+=q[i].d;
	else bucket2[q[i].t+n]+=q[i].d;
  }
  for(int i=first[v];i;i=e[i].next)
    if (dep[e[i].v]>dep[v]) find_ans(e[i].v);
  ans[v]=bucket1[x1]+bucket2[x2]-val1-val2;
}

int main()
{
  scanf("%d%d",&n,&m);
  for(int i=1,a,b;i<n;i++)
  {
    scanf("%d%d",&a,&b);
	insert(a,b),insert(b,a);
  }
  for(int i=1;i<=n;i++)
    scanf("%d",&w[i]);
  
  dep[1]=0;dep[0]=-1;
  dfs(1);
  for(int i=1;i<=19;i++)
    for(int j=1;j<=n;j++)
	  fa[j][i]=fa[fa[j][i-1]][i-1];
  
  tot=0;
  for(int i=1,a,b;i<=m;i++)
  {
    scanf("%d%d",&a,&b);
    int f=lca(a,b);
	insertq(a,0,1,0),insertq(b,dep[a]-2*dep[f],1,1);
	insertq(fa[f][0],dep[a]-dep[f]+1,-1,0),insertq(f,dep[a]-2*dep[f],-1,1);
  }
  
  find_ans(1);
  for(int i=1;i<=n;i++)
    printf("%d ",ans[i]);
  
  return 0;
}


相關推薦

NOIP2016提高T2天天跑步-倍增LCA+樹上

測試地址:天天愛跑步 做法:這裡轉載一下我看的題解:點這裡,這裡面對於整個題的做法應該寫的很明確了,這裡就不再贅述了。我的做法是實時用倍增求出路徑兩點的LCA(當然也可以離線用Tarjan做,貌似快一點),然後用類似鄰接表的連結串列結構儲存上面題解裡面的“人”,結構體裡有四

NIOIP2016提高天天跑步LCA+樹上

近幾年複賽最難的樹上問題了。 幾個月前做是參照題解的方法,用了可持久化線段樹在樹上無腦維護和統計。 當時的做法早已忘記,於是回過來自己做了做,其實遠沒有那麼難做,只要發現一些奇妙的性質。 對於一個玩家s->t,如圖。 對於圖中a點的觀察員存在這樣一個式子

NOIP2016提高day2蚯蚓

AC AS orm max 可能 printf display OS 例如 題目 本題中,我們將用符號 LcJ 表示對 c 向下取整,例如: L3.0J = L3.1J = L3.9J = 3 。 蛐蛐國最近蚯蚓成災了!隔壁跳蚤國的跳蚤也拿蚯蚓們沒辦法,蛐蛐國王只好去 請神

NOIP2016提高day1?換教室

獲得 表示 牛牛 所在 進行 發現 更換 學校 而且 題目 對於剛上大學的牛牛來說,他面臨的第一個問題是如何根據實際情況申請合適的 課程。 在可以選擇的課程中,有 2n 節課程安排在 n 個時間段上。 在第 i ( 1 ≤ i ≤ n )個 時間段上,兩節內容相同的課程同時

NOIP2016提高day2憤怒的小鳥

span math cst clas 解析式 ++ amp -- memset 分析 Kiana最近沈迷於一款神奇的遊戲無法自拔。 簡單來說,這款遊戲是在一個平面上進行的。 有一架彈弓位於 (0, 0) 處,每次Kiana可以用它向第一象限發射一只紅色的小鳥, 小鳥們的飛行

NOIP2016提高複賽憤怒的小鳥

Description Solution 很明顯是一個狀態壓縮DP的題目,不過也可以打成記憶化搜尋。 設f[i]表示每個點選或不選的狀態投射小鳥的最少方案。 首先預處理一個g[i][j]表示

NOIP2016提高復賽day2天天跑步

分享 csdn .net ons src 情況 自己的 for net 題目 小 C 同學認為跑步非常有趣,於是決定制作一款叫做《天天愛跑步》的遊戲。 《天天愛跑步》是一個養成類遊戲,需要玩家每天按時上線,完成打卡任務。 這個遊戲的地圖可以看作一棵包含 n 個結點和 n ?

NOIP2016提高天天跑步

題目背景 NOIP2016 提高組 Day1 T2 題目描述 小 C 同學認為跑步非常有趣,於是決定製作一款叫做《天天愛跑步》的遊戲。《天天愛跑步》是一個養成類遊戲,需要玩家每天按時上線,完成打卡任務。 這個遊戲的地圖可以看作一一棵包含 n 個結點和 n-1 條邊的樹

NOIP2016提高Day1天天跑步

魔鬼題面 題解: LCA + 樹上差分。對於一次詢問u到v,我們可以拆成兩段,一段為u到LCA,一段為LCA到v。 先考慮u到LCA,即從下到上的情況。對於在i點的觀察員,只有深度在Depth[i] + W[i]的點才可能對i點有貢獻,這個貢獻在LCA點結束。那麼利用差分思想,我們在De

NOIP2016提高天天跑步的解析(樹上+LCA+桶)

題目:luogu1600. 題目大意:給定一棵樹和樹上每個節點的,現在給出m對和,表示從到會有一個人沿樹上的路徑走過,並且這個人每秒移動到下一個點.現在每個人都在時刻0走出,詢問每一個點i,請你輸出第時刻有多少人會在點i. 這道題真的是史上最難的NOIP題啊... 我們現在一步步分析

NOIP2016天天跑步

truct ios 多少 個人 img 自己 ret 一行 速度 題目描述 小c同學認為跑步非常有趣,於是決定制作一款叫做《天天愛跑步》的遊戲。?天天愛跑步?是一個養成類遊戲,需要玩家每天按時上線,完成打卡任務。 這個遊戲的地圖可以看作一一棵包含 個結點和 條邊的樹, 每條

NOIP2016提高憤怒的小鳥(狀壓寬搜)

數組保存 結果 4.0 2.0 pac 之前 第一個 ... 預處理 題目描述 Kiana最近沈迷於一款神奇的遊戲無法自拔。 簡單來說,這款遊戲是在一個平面上進行的。 有一架彈弓位於(0,0)處,每次Kiana可以用它向第一象限發射一只紅色的小鳥,小鳥們的飛行軌跡均為形如的

NOIP2016提高換教室

algorithm .org pan i++ mes ble 安排 turn 路徑 https://www.luogu.org/problem/show?pid=1850 題面很長,實質很水的一道期望DP題。題面自帶勸退效果。 首先用Floyd算出任意兩點的最短路徑。然後設

NOIP2016提高 Day1 T3 換教室

scanf spa -s 提高 i++ double -1 教室 pan 題目鏈接:https://www.luogu.org/problemnew/show/P1850 此題正解為dp。 我們先用floyd處理出任意兩個教室之間的距離,用dis[i][j]表示。

NOIP2016提高A7.16第三條跑道

以及 span const queue als TP 分析 namespace 技術分享 題目 數據範圍 分析 時限5000ms。 我們註意到\(a_{i}初始值以及x小於等於600且非零\) 也就是說,\(a_{i}\)的質因數一定小於600,而600以內的質因數只有

2018.11.09NOIP2016洛谷P1600天天跑步樹上

傳送門 解析: 據說這是NOIP歷年最難一道題。。但是真的沒有寶藏難啊我覺得。。。 思路: 答案分兩類統計,一種是子樹中過來,一種是其他地方過來。那麼路徑就被拆分成兩部分了,一部分是S−&gt;lcaS-&gt;lcaS−>lca,一部分

BZOJ4719NOIP2016天天跑步

【題目連結】【思路要點】補檔部落格,無題解。【程式碼】#include<bits/stdc++.h> using namespace std; #define MAXN 300005 #define MAXV 600005 #define MAXLOG 20 te

jzoj4908NOIP2016提高憤怒的小鳥狀態壓縮動態規劃

題目大意 解題思路 由於點的個數很少可以狀態壓縮,我們可以找到第一個沒有覆蓋的點,再隨便列舉一個點(記得打break),兩個點就可以確定一條拋物線,再掃一遍就可以知道有哪些點被覆蓋,

LCA+樹上天天跑步

blog 分支 div fin out 答案 簡單 printf 但是 困擾我半年多的題終於做出來了 一開始我的做法是想在回溯的時候統計答案,但是各個分支之間又會相互影響,然後就不會做了 看完別人的題解後發現用桶的前後狀態做差來統計答案更簡單 1 #include &l

NOIP2017提高 day2寶藏

題目 題解 –這道題不是最小生成樹這道題不是最小生成樹這道題不是最小生成樹 因為修路的代價與它裡起點的距離有關,又因為最後的路徑一定組成了一棵樹 所以我們可以一層層的dp 設f[i][s]:到第i層時,已經連上的集合為s的最優解 轉移f[i][s|S]=min(f