icpc2019 香港 C. Constructing Ranches (点分治)

题目链接:https://codeforces.com/gym/102452/problem/C

\(n\) 条边能够形成多边形的结论是所有边之和大于最长的边

于是点分治,维护每个点到根的路径上的点权和和点权最大值,统计满足 \(sum[u]+sum[v]-a[rt]>2\times max\{mx[u], mx[v]\}\),的点对\((u,v)\)的数量,考虑将所有点的 \(sum\)\(mx\) 排序,这样从小到大扫描时保证当前点 \(u\)\(mx\) 为最大值,然后树状数组统计满足 \(sum[v] > 2*mx[u]-sum[u]+a[rt]\) 的点 \(v\) 的数量,容斥即可

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

const int maxn = 400010;
const ll INF = 1e18+7;

int T, n, m;
ll a[maxn], ans;

int h[maxn], cnt = 0;
struct E{
	int to, next;
}e[maxn<<1];
void add(int u, int v){
	e[++cnt].to = v;
	e[cnt].next = h[u];
	h[u] = cnt;
} 

int rt, tot;
int sz[maxn], son[maxn], vis[maxn], maxson;

void getrt(int u, int par){
	sz[u] = 1; son[u] = 0;
	for(int i = h[u] ; i != -1 ; i = e[i].next){
		int v = e[i].to;
		if(vis[v] || v == par) continue;
		getrt(v, u);
		sz[u] += sz[v];
		son[u] = max(son[u], sz[v]);
	}
	son[u] = max(son[u], tot - sz[u]);
	if(son[u] < maxson){
		maxson = son[u];
		rt = u;
	}
}

struct Node{
	int id;
	ll dis, mx;
	
	bool operator < (const Node & x) const{
		return mx < x.mx;
	}
}p[maxn];

int num = 0;
ll dis[maxn], mx[maxn], b[maxn];
void getdis(int u, int par){
	++num;
	p[num].id = u, p[num].dis = dis[u], p[num].mx = mx[u];
	b[num] = dis[u];
	for(int i = h[u] ; i != -1 ; i = e[i].next){
		int v = e[i].to;
		if(vis[v] || v == par) continue;
		dis[v] = dis[u] + a[v];
		mx[v] = max(mx[u], a[v]);
		getdis(v, u);
	}
}

int q;
ll c[maxn];

void ad(int x, int val){
	for(int i = x ; i <= 200000 ; i += i&(-i)){
		c[i] += val;
	}
}

ll sum(int x){
	ll res = 0;
	for(int i = x ; i ; i -= i & (-i)){
		res += c[i];
	}
	return res;
}

void calc(int u){
	num = 0;
	dis[u] = a[u], mx[u] = a[u];
	getdis(u, 0);

	sort(p+1, p+1+num);

	b[num+1] = INF;
	sort(b+1, b+1+num+1);
	q = unique(b+1, b+1+num+1)-b-1;
	for(int i = 1 ; i <= num ; ++i) {
		p[i].dis = lower_bound(b+1, b+1+q, p[i].dis)-b;
	}
	
	for(int i = 1 ; i <= num ; ++i){
		int pos = upper_bound(b+1, b+1+q, 2ll*p[i].mx-b[p[i].dis]+a[u])-b;
		ans += sum(q) - sum(pos-1);
		ad(p[i].dis, 1);
	}
	
	for(int i = 1 ; i <= num ; ++i){
		ad(p[i].dis, -1);
	}
	
	for(int i = h[u] ; i != -1 ; i = e[i].next){
		int v = e[i].to;
		if(vis[v]) continue;

		num = 0;
		dis[v] = a[u]+a[v];
		mx[v] = max(a[u], a[v]);
		getdis(v, u);
		
		sort(p+1, p+1+num);
		
		b[num+1] = INF;
		sort(b+1, b+1+num+1);
		q = unique(b+1, b+1+num+1)-b-1;
		
		for(int j = 1 ; j <= num ; ++j) {
			p[j].dis = lower_bound(b+1, b+1+q, p[j].dis)-b;
		}
		for(int j = 1 ; j <= num ; ++j){
			int pos = upper_bound(b+1, b+1+q, 2ll*p[j].mx-b[p[j].dis]+a[u])-b;
			ans -= sum(q) - sum(pos-1);
			ad(p[j].dis, 1);
		} 
		
		for(int j = 1 ; j <= num ; ++j){
			ad(p[j].dis, -1);
		}
	}
}

void divi(int u){
	calc(u);
	vis[u] = 1;
	
	for(int i = h[u] ; i != -1 ; i = e[i].next){
		int v = e[i].to;
		if(vis[v]) continue;
		maxson = 1000000007, tot = sz[v];
		getrt(v, u);
		divi(rt);
	}
}

void solve(){
	maxson = 1000000007, tot = n;
	getrt(1, 0);
	divi(rt);
}

ll read(){ ll s = 0, f = 1; char ch = getchar(); while(ch < '0' || ch > '9'){ if(ch == '-') f = -1; ch = getchar(); } while(ch >= '0' && ch <= '9'){ s = s * 10 + ch - '0'; ch = getchar(); } return s * f; }

int main(){
	T = read();
	while(T--){
		ans = 0;
		memset(h, -1, sizeof(h));
		n = read();
		for(int i = 1 ; i <= n ; ++i) vis[i] = 0;
		for(int i = 1 ; i <= n ; ++i) a[i] = read();
		int u, v;
		for(int i = 1 ; i < n ; ++i){
			u = read(), v = read();
			add(u, v); add(v, u);
		}
		
		solve();
		
		printf("%lld\n", ans);
	}
	return 0;
}
posted @ 2021-09-18 15:10  Tartarus_li  阅读(70)  评论(0编辑  收藏  举报