[bzoj3991][SDOI2015]尋寶遊戲 lca+set+dfs序

3991: [SDOI2015]尋寶遊戲

Time Limit: 40 Sec  Memory Limit: 128 MB
Submit: 1379  Solved: 671
[Submit][Status][Discuss]

Description

 B最近正在玩一個尋寶遊戲,這個遊戲的地圖中有N個村莊和N-1條道路,並且任何兩個村莊之間有且僅有一條路徑可達。遊戲開始時,玩家可以任意選擇一個村莊,瞬間轉移到這個村莊,然後可以任意在地圖的道路上行走,若走到某個村莊中有寶物,則視爲找到該村莊內的寶物,直到找到所有寶物並返回到最初轉移到的村莊爲止。B希望評測一下這個遊戲的難度,因此他需要知道玩家找到所有寶物需要行走的最短路程。但是這個遊戲中寶物經常變化,有時某個村莊中會突然出現寶物,有時某個村莊內的寶物會突然消失,因此小B需要不斷地更新數據,但是小B太懶了,不願意自己計算,因此他向你求助。爲了簡化問題,我們認爲最開始時所有村莊內均沒有寶物

Input

 第一行,兩個整數N、M,其中M爲寶物的變動次數。

接下來的N-1行,每行三個整數x、y、z,表示村莊x、y之間有一條長度爲z的道路。
接下來的M行,每行一個整數t,表示一個寶物變動的操作。若該操作前村莊t內沒有寶物,則操作後村莊內有寶物;若該操作前村莊t內有寶物,則操作後村莊內沒有寶物。

Output

 M行,每行一個整數,其中第i行的整數表示第i次操作之後玩家找到所有寶物需要行走的最短路程。若只有一個村莊內有寶物,或者所有村莊內都沒有寶物,則輸出0。

Sample Input

4 5
1 2 30
2 3 50
2 4 60
2
3
4
2
1

Sample Output

0
100
220
220
280

HINT

 1<=N<=100000


1<=M<=100000

對於全部的數據,1<=z<=10^9

Source

這道題知道怎麼做後是比較簡單的
開始我想的是樹鏈剖分+01線段樹,再用set維護所有數
但是那個set是沒有排序依據的
在這道題中按dfs序排序
首先它要求的是從一些點的一個點到點集中所有點再回來的最小距離
首先我們要知道我們一個點到另一個點最短路徑唯一確定
然後我們假想從一個節點出發是要回到這個節點的,
所以一條要經過的邊最少是要走兩次的,且必須經過
那麼如何讓我們每一條邊之走兩次呢?
我們想到在dfs一棵樹的時候,我們每一個節點求一個in和out
也就是說,我們每一條邊只走了兩次dfs了一棵樹
那麼我們使dfs序排在第一的點作爲起點,dfs序在最後的點作爲最後一個被訪問的點
set用dfs序作爲順序
然後我們維護的答案就是起點經過所有點到終點的距離,再加上起點到終點的距離
注意這不是唯一方案,只是最優方案之一
我把深度寫成16還A了
數據太水了
數據太水了
數據太水了
#include<iostream>
#include<cstring>
#include<cstdio>
#include<set>
using namespace std;
const int N = 100000 + 5;
typedef long long ll;
int cnt,n,m,sz,opt; bool vis[N];
int last[N],dep[N],anc[N][20],in[N],inv_in[N],siz[N];
ll d[N],ans;
struct Edge{ int to, next, v; }e[N<<1];
void insert( int u, int v, int w ){
	e[++cnt].to = v; e[cnt].next = last[u]; e[cnt].v = w; last[u] = cnt;
	e[++cnt].to = u; e[cnt].next = last[v]; e[cnt].v = w; last[v] = cnt;
}
void dfs( int x, int f ){
	siz[x] = 1; in[x] = ++sz; inv_in[sz] = x;
	for( int p = 1; p <= 16; p++ ) anc[x][p] = anc[anc[x][p-1]][p-1];
	for( int i = last[x]; i; i = e[i].next )
		if( e[i].to ^ f ){
			dep[e[i].to] = dep[x]+1;
			d[e[i].to] = d[x] + e[i].v;
			anc[e[i].to][0] = x;
			dfs( e[i].to, x );
			siz[x] += siz[e[i].to];
		}
}
int lca( int x, int y ){
	if( dep[x] < dep[y] ) swap(x,y);
	int t = dep[x] - dep[y];
	for( int p = 0; p <= 16; p++ )
		if( (1<<p)&t )
			x = anc[x][p];
	if( x == y ) return x;
	for( int p = 16; p >= 0; p-- )
		if( anc[x][p] != anc[y][p] )
			x = anc[x][p], y = anc[y][p];
	return anc[x][0];
}
ll dis( int x, int y ){
	return d[x] + d[y] - 2*d[lca( x, y )];
}
set <int> q;
set <int> :: iterator it;
int next( int x ){
	it = q.find(in[x]);
	return ++it == q.end() ? 0 : inv_in[*it];
}
int pre( int x ){
	it = q.find(in[x]);
	return it == q.begin() ? 0 : inv_in[*--it];
}
void set_erase( int x ){
	int pr = pre(x), ne = next(x);
	q.erase(in[x]);
	if( pr ) ans -= dis( pr, x );
	if( ne ) ans -= dis( x, ne );
	if( pr && ne ) ans += dis( pr, ne );
}
void set_insert( int x ){
	q.insert(in[x]);
	int pr = pre(x), ne = next(x);
	if( pr ) ans += dis( pr, x );
	if( ne ) ans += dis( x, ne );
	if( pr && ne ) ans -= dis( pr, ne );
}
int main(){
	scanf( "%d%d", &n, &m );
	for( int i = 1,u,v,w; i < n; i++ ){
		scanf( "%d%d%d", &u, &v, &w );
		insert( u, v, w );
	}
	dfs( 1, 0 );
	while( m-- ){
		int x;
		scanf( "%d", &x );
		if( vis[x] ) set_erase(x), vis[x] = 0;
		else set_insert(x), vis[x] = 1;
		if( !q.size() ) puts("0");
		else printf( "%lld\n", ans + dis( inv_in[*q.begin()], inv_in[*--q.end()] ) );
	}
	return 0;
}


數據太水了
數據太水了
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章