我們考慮現在已知點集,如何求最後的答案。發現答案就是求所有點構成的最小生成樹的邊權和減去最小生成樹直徑。
嗯……這個東西我好像不會動態維護啊。
哦,發現要回到最初轉移到的村莊,那不就是兩倍的最小生成樹的邊權嗎,題目變簡單了一點。
現在就是考慮動態維護最小生成樹的邊權和,我們考慮將邊權下放。那麼當前這棵樹的答案就是最小生成樹的點權和減去最上面的點的點權。
最小生成樹的點權和可以用掃瞄線來搞。然後最上面的點就是所有點的公共 \(lca\) , \(emm...\),這個東西可以用線段樹維護嗎?
應該是可以的,每一次查詢兩個點的 \(lca\) 用 \(st\) 表,然後兩兩合併,單點修改是否存在,好像很可做。
然後這道題目就被我口胡出來了???
發現 \(lca\) 的部分沒有問題,但是在求最小生成樹點權的時候的掃瞄線好像是假的(前面做了一道很像的題,用掃瞄線可以搞,但這裡發現不可以)。所以我們考慮如何給你一堆點,求這些點的最小生成樹。
發現這是一道虛樹的題而我以前從來都沒有做過虛樹啊……
所以說現在我們的目標就是考慮如何動態維護最小生成樹的點權和……
我在處理連邊的時候想到了鼠樹,然後就知道了 \(ett\) 。
跟巨佬 \(\text\) 交流過之後發現好像可以不用什麼稀奇古怪的東西來做了。
這裡要講的是乙個兩隻 \(\log_2\) 的低劣做法,僅供各位參考,希望能給您帶來啟發。
問題轉化為動態求乙個點集的最小生成樹的邊權和,發現這個東西不太好弄。
我們考慮到邊權不是很好維護,所以就將邊權下放點權,那麼最終求答案的時候就需要兩個東西:最小生成樹的點權和和生成樹最上方的點的點權(因為最上面的這個東西不在答案範圍中)。
我們先來考慮後者,發現最上方的點實際上就是所有節點的 \(lca\)(注:此處是指所有點的最近公共祖先),也就是要動態維護乙個點集的 \(lca\) 。
可以發現如果將 \(lca(u,v)\) 看成一種運算,發現他是完美滿足結合律的,然後我們就可以用線段樹來維護,這裡不多贅述了。
然後考慮前者,發現這個東西除非用一些動態的高妙資料結構好像根本不能維護,但是我們可以發現,如果我們用類似於掃瞄線的做法,每一次加入或刪除乙個點的時候,將這個點到根的路徑的 \(tag\) 全部 \(+1\) ,那麼最後查詢的整棵樹中 \(tag\) 大於 \(0\) 的節點的權值和,實際上就是我們最後需要的最小生成樹的點權和加上所有點的 \(lca\) 到根節點的路徑的權值和,由於我們前面已經可以維護所有點的 \(lca\) 了,所有最後只需要減去後者(所有點的 \(lca\) 到根節點的路徑的權值和)即可。
#includeusing namespace std;
#define lint long long
const int n=1e5+5;
int n,m;
struct nodetr[n];
struct edgee[n<<1];int fir[n];
void add(int u,int v,lint w,int i),fir[u]=i;}
void dfs1(int u)
}int dfn[n],mp[n],cnt=0;
void dfs2(int u)
for(int i=fir[u];i;i=e[i].nxt) }
int lca(int u,int v)
bool alive[n];
lint val[n];
struct seg_tree_data
tr[n<<3];
void up(int u)
void build(int u,int l,int r,lint a)
int mid=(l+r)>>1;
build(u<<1,l,mid,a);
build(u<<1|1,mid+1,r,a);
tr[u].sum=tr[u<<1].sum+tr[u<<1|1].sum;
} void add(int u,int l,int r,int x,int y,int z)
int mid=(l+r)>>1;
if(x<=mid) add(u<<1,l,mid,x,y,z);
if(y>mid) add(u<<1|1,mid+1,r,x,y,z);
up(u);
}}t1;
struct seg_tree_lca
tr[n<<2];
void up(int u)
if(!tr[u<<1|1].data)
tr[u].data=lca(tr[u<<1].data,tr[u<<1|1].data);
} void chg(int u,int l,int r,int x)
int mid=(l+r)>>1;
if(x<=mid) chg(u<<1,l,mid,x);
else chg(u<<1|1,mid+1,r,x);
up(u);
}}t2;
void work(int u,int v,int w)
int main()
else
// printf("%lld %d\n",t1.tr[1].data,t2.tr[1].data);
printf("%lld\n",2*(t1.tr[1].data-tr[t2.tr[1].data].dis));
} return 0;
}
P3320 SDOI2015 尋寶遊戲 題解
題面 需要動態維護乙個點集的極小聯通子圖邊權和。可以發現,將點集 中的點按照 dfs 序從小到大排序之後,dist a 1,a 2 dist a 2,a 3 ldots dist a a k dist a k,a 1 恰好等於我們要維護的那個邊權和的兩倍。所以就開乙個set,在加入和刪除的時候加上或...
LG3320 SDOI2015 尋寶遊戲
洛谷 不需要建虛樹的虛樹2333。貪心地想一下,起始節點肯定是在關鍵點上,訪問順序就是 dfs 序。那麼對於每次詢問,ans dis s 1,s s sum dis s i,s 用 set 維護一下就好了 include include include include include include...
Luogu 3320 SDOI2015 尋寶遊戲
一開始還真沒想到。發現從所有有寶藏的點出發繞一圈只要不刻意繞路答案都是一樣的,即我們呢要求的最後答案 ans dis x 1,x 2 dis x 2,x 3 dis x x k dis x k,x 1 不刻意繞遠路怎麼辦呢,我們把有寶藏的點按照 dfs 序,維護乙個有序的 set 就可以了。每次插入...