luogu6584 重拳出擊


題目鏈接

solution

為了方便,我們以小\(z\)的初始位置為根。

大概理解完題意,可以發現有一個比較顯然的性質:每一回合結束,每個\(Youyou\)與小\(z\)之間的距離不會變大。

然后考慮小\(z\)移動所產生的影響。

考慮當小\(z\)開始移動時,如果小\(z\)\(u\)移動到了\(u\)的一個兒子\(v\)。那么這一回合結束,\(v\)子樹中的每個Youyou與小z之間的距離就會減小2。其他的\(Youyou\)與小\(z\)之間的距離不變。

如果小\(z\)\(u\)移動到了他的父親\(fa\),那么除了\(u\)這個子樹內的\(Youyou\)外,其他的\(Youyou\)與小\(z\)之間的距離都會減小\(2\)

然后我們考慮貪心,初始的時候每個\(Youyou\)與小\(z\)之間的距離就是\(Youyou\)所在節點的深度。我們每次讓小\(z\)走向離他最遠的那個\(Youyou\)

這樣顯然是對的,因為如果小\(z\)不走向這個最遠的\(Youyou\),這個\(Youyou\)與小\(z\)之間的距離將永遠是最大的。

那當有若干個與小\(z\)之間距離相同的\(Youyou\)時,應該如何決策呢?

走向任意一個即可。假設\(y_1\)\(y_2\)與小\(z\)當前所在節點之間的距離都是x且是最大的,而且小\(z\)走向\(y_1\)\(y_2\)不在小\(z\)的同一個方向上。如果我們先走向了\(y_1\),那么y1與小z之間的距離減小\(2\),下一回合的時候最大的點就是\(y_2\)了,所以下一回合就要走向\(y_2\),也就是回到之前的點。這樣就相當於進行了兩次操作,小\(z\)位置沒變,與\(y_1\)\(y_2\)之間的距離都減小了\(2\).如果先走向\(y_2\)顯然也是同樣的情況。

所以這題思路也就理順了。下面就是如何實現的問題了。

看一看思路中我們需要進行的操作,發現只有下面這幾種:

  • 將一個子樹每個節點權值-2
  • 求整顆子樹中最大值所在的位置
  • 求整棵樹的權值最大值
  • 求某個節點x在另外一個節點\(y\)的哪個方向上。也就是說,如果要從\(y\)走向\(x\)下一步應該走向哪個節點。

我們前三個操作就\(dfs\)序一下,然后用一個線段樹維護區間加,區間最大值即可。

對於最后一個操作其實也很簡單。我們分為兩種情況。

下面假設我們要從\(y\)走向\(x\)

如果\(x\)不在\(y\)的子樹中,顯然下一步走向\(y\)的父親即可。

如果\(x\)\(y\)的子樹中,我們就用倍增的方法,從\(x\)開始向上跳,一直跳到深度比\(y\)大一即可。

這樣我們就基本解決了這道題,剩下的就按照題目給的流程來操作即可。

注意一個坑點

如果當前與小\(z\)之間距離最大的幾個點與小\(z\)之間的距離都是\(k+1\),那么這一輪應當不動

code

/*
* @Author: wxyww
* @Date:   2020-05-30 16:25:49
* @Last Modified time: 2020-05-30 19:12:59
*/
#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<queue>
#include<vector>
#include<ctime>
#include<cmath>
using namespace std;
typedef long long ll;
const int N = 400010,logN = 20;
ll read() {
	ll x = 0,f = 1;char c = getchar();
	while(c < '0' || c > '9') {
		if(c == '-') f = -1; c = getchar();
	}
	while(c >= '0' && c <= '9') {
		x = x * 10 + c - '0'; c = getchar();
	}
	return x * f;
}
int bz[N];
struct node {
	int v,nxt;
}e[N << 1];

int head[N],ejs;
void add(int u,int v) {
	e[++ejs].v = v;e[ejs].nxt = head[u];head[u] = ejs;
}

int n,K,siz[N],dep[N],id[N],dfn[N],tot;

int tree[N << 2];

int lca[N][logN + 2];
void dfs(int u,int fa) {
	dfn[u] = ++tot;
	dep[u] = dep[fa] + 1;
	id[tot] = u;
	siz[u] = 1;
	for(int i = 1;i <= logN;++i) {
		lca[u][i] = lca[lca[u][i - 1]][i - 1];
	}

	for(int i = head[u];i;i = e[i].nxt) {
		int v = e[i].v;
		if(v == fa) continue;
		lca[v][0] = u;
		dfs(v,u);
		siz[u] += siz[v];
	}
}
int lazy[N << 2];

void pushdown(int rt) {
	if(lazy[rt] != 0) {
		tree[rt << 1] += lazy[rt];
		tree[rt << 1 | 1] += lazy[rt];
		lazy[rt << 1] += lazy[rt];
		lazy[rt << 1 | 1] += lazy[rt];
		lazy[rt] = 0;
	}
}

void build(int rt,int l,int r) {
	if(l == r) {
		if(bz[id[l]]) tree[rt] = dep[id[l]];
		return;
	}
	int mid = (l + r) >> 1;
	build(rt << 1,l,mid);build(rt << 1 | 1,mid + 1,r);
	tree[rt] = max(tree[rt << 1],tree[rt << 1 | 1]);
}

void update(int rt,int l,int r,int L,int R,int c) {
	if(L <= l && R >= r) {
		tree[rt] += c;lazy[rt] += c;
		return;
	}

	pushdown(rt);
	int mid = (l + r) >> 1;
	if(L <= mid) update(rt << 1,l,mid,L,R,c);
	if(R > mid) update(rt << 1 | 1,mid + 1,r,L,R,c);
	tree[rt] = max(tree[rt << 1],tree[rt << 1 | 1]);
}

int query(int rt,int l,int r) {
	if(l == r) return id[l];
	pushdown(rt);
	int mid = (l + r) >> 1;
	if(tree[rt << 1] >= tree[rt << 1 | 1]) return query(rt << 1,l,mid);
	else return query(rt << 1 | 1,mid + 1,r);
}

int get(int x,int depth) {
	for(int i = logN;i >= 0;--i) {
		if(dep[lca[x][i]] >= depth) x = lca[x][i];
	}
	return x;
}

int main() {
	n = read();
	for(int i = 1;i < n;++i) {
		int u = read(),v = read();
		add(u,v);add(v,u);
	}
	memset(tree,-0x3f,sizeof(tree));
	int m = read();
	if(!m) {puts("0");return 0;}
	for(int i = 1;i <= m;++i) {
		int x = read();
		bz[x] = 1;
	}

	K = read();int root = read();
	dep[0] = -1;
	dfs(root,0); build(1,1,n);
	int ans = 0;

	while(1) {
		++ans;
		if(tree[1] <= K) {
			cout<<ans;return 0;
		}
		if(tree[1] - K == 1) {
			update(1,1,n,1,n,-1);
			continue;
		}
		int p = query(1,1,n);

		if(dfn[p] >= dfn[root] && dfn[p] < dfn[root] + siz[root]) {
			p = get(p,dep[root] + 1);
			update(1,1,n,dfn[p],dfn[p] + siz[p] - 1,-2);
			root = p;
		}
		else {
			update(1,1,n,dfn[root],dfn[root] + siz[root] - 1,2);
			update(1,1,n,1,n,-2);
			root = lca[root][0];
		}
	}
	return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM