DTOJ#5228. 划分

传送门

给定一棵 n n n 个节点,以 1 1 1 为根的有根树,每个点有一个权值 w i w_i wi。你可以把所有点
分进两个集合 A A A B B B 中,定义一种划分方案的代价为:
∑ x ∈ A ∑ y ∈ A ( ( F ( x , y ) ∧ w x > w y ) ∨ G ( x , y ) ) + ∑ x ∈ A d x + ∑ x ∈ B ∑ y ∈ B ( F ( x , y ) ∧ w x < w y ) \sum_{x \in A} \sum_{y \in A} ((F(x, y) \land w_x > w_y) \lor G(x, y)) + \sum_{x \in A} d_x + \sum_{x \in B} \sum_{y \in B} (F(x, y) \land w_x < w_y) xAyA((F(x,y)wx>wy)G(x,y))+xAdx+xByB(F(x,y)wx<wy)
其中 d x d_x dx 表示 x x x 的深度 (到根的距离), F ( x , y ) F(x, y) F(x,y) 表示 x x x 是否是 y y y 的祖先,是则为 1 1 1,否则为 0 0 0 G ( x , y ) G(x, y) G(x,y) 表示是否满足 x < y x < y x<y x , y x, y x,y 没有祖先关系,是则为 1 1 1,否则为 0 0 0

请你对于每个 0 ≤ i ≤ n 0 \le i \le n 0in 求出满足 ∣ B ∣ = i \lvert B \rvert = i B=i 的前提下代价最小划分方案的代价,并输出。

第一行包含一个整数 n n n,表示点数。

接下来一行 N N N 个整数 w i w_i wi,表示每个点的权值。

接下来 N − 1 N − 1 N1 行,每行两个整数 u , v u, v u,v,表示一条 u u u v v v 的边。

输出 n + 1 n + 1 n+1 行每行一个整数,第 i i i 行表示 ∣ B ∣ = i − 1 \lvert B \rvert = i − 1 B=i1 时的答案。

样例输入

4
4 1 2 3
1 2
2 3
2 4

样例输出

9
5
2
1
2

对于所有数据,保证 1 ≤ n , w i ≤ 5 × 1 0 5 , 1 ≤ u , v ≤ n 1 \leq n, w_i \leq 5 \times 10^5, 1 \leq u, v \leq n 1n,wi5×105,1u,vn
subtask 1 (10pts): N ≤ 20 N \leq 20 N20;
subtask 2 (20pts): N ≤ 500 N \leq 500 N500;
subtask 3 (35pts): w i w_i wi 互不相同;
subtask 4 (35pts): 没有其它限制。
题解:
首先,我们把柿子化成:
∣ A ∣ × ( ∣ A ∣ − 1 ) − ∑ x ∈ A ∑ y ∈ A ( F ( x , y ) ∧ w x ≤ w y ) + ∑ x ∈ A d x + ∑ x ∈ B ∑ y ∈ B ( F ( x , y ) ∧ w x < w y ) |A|\times(|A|-1)-\sum_{x \in A} \sum_{y \in A} (F(x, y) \land w_x \leq w_y)+ \sum_{x \in A} d_x + \sum_{x \in B} \sum_{y \in B} (F(x, y) \land w_x < w_y) A×(A1)xAyA(F(x,y)wxwy)+xAdx+xByB(F(x,y)wx<wy)
然后发现有个长得很像的柿子。这时思考为什么符号不一样。加号和减号,十分类似进集合和出集合。
那么思路就来了。
考虑先求出 ∣ B ∣ = n |B|=n B=n。(因为 B B B 的柿子短)
那答案就 dfs 时用树状数组维护。
假设所有点权值不同。
考虑差值:设 x x x B B B A A A
那么 △ = \triangle = = d x + d_x+ dx+ ( i , x ) (i,x) (i,x)为祖先关系且祖先小于儿子的对数。这是一个常数。直接从大往小取。
当存在点权值相同时。
因为差距只在存在祖孙关系的点,那么微扰相邻。发现若父亲比儿子先进 A A A,设 a x a_x ax 表示 x x x 祖先中比它小的个数, b x b_x bx 表示子孙中大于 x x x 的个数。那么对于父亲 u u u 和 子孙 v v v a v − a u ≤ d v − d u a_v-a_u\leq d_v-d_u avaudvdu b v ≤ b u b_v\leq b_u bvbu,而且深度越小子孙越多,对 A A A 的贡献越大。所以一定先加祖先。
那么设 c x c_x cx 表示祖先中权值等于 x x x 的个数。按照 d x − a x − b x − c x d_x-a_x-b_x-c_x dxaxbxcx 贪心取就可以了。

#include<bits/stdc++.h>
#define N 500005
typedef long long ll;
using namespace std;
int read(){
    
    
	int op=1,sum=0;char ch=getchar();
	while(ch<'0'||ch>'9') {
    
    if(ch=='-') op=-1;ch=getchar();}
	while(ch>='0'&&ch<='9') sum=(sum<<3)+(sum<<1)+ch-'0',ch=getchar();
	return op*sum;
}
ll w[N],dep[N],a[N],b[N],c[N];
int dfn[N],dfs_num,n,sz[N],q[N];
vector<int> to[N];
const int inf=5e5;
struct BIT{
    
    
	ll t[N];
	inline void init(){
    
    
		memset(t,0,sizeof(t));
	}
	inline ll get(int x){
    
    
		ll now=0;
		for(;x;x-=(x&-x))now+=t[x];
		return now;
	}
	inline void add(int x,ll val){
    
    
		for(;x<=inf;x+=(x&-x))t[x]+=val;
	}
}T[3];
void dfs(int x,int las){
    
    
	dfn[x]=++dfs_num;
	q[dfs_num]=x;
	a[x]+=T[0].get(w[x]-1);
	c[x]+=T[0].get(w[x])-a[x];
	sz[x]=1;
	T[0].add(w[x],1);
	for(int i=0;i<to[x].size();++i){
    
    
		int y=to[x][i];
		if(y==las)continue;
		dep[y]=dep[x]+1;
		dfs(y,x);
		sz[x]+=sz[y];
	}
	T[0].add(w[x],-1);
}
struct node{
    
    
	int l,x,op,id;
}qu[N<<1];
bool cmp(node a,node b){
    
    return a.l<b.l;}
int qt;
ll ans[N];
priority_queue<ll> Q;
int main(){
    
    
	n=read();
	for(int i=1;i<=n;++i)w[i]=read();
	for(int i=1;i<n;++i){
    
    
		int x=read(),y=read();
		to[x].push_back(y);to[y].push_back(x);
	}
	T[0].init();
	dep[1]=1;
	dfs(1,0);
	for(int i=1;i<=n;++i){
    
    
		qu[++qt].l=dfn[i],qu[qt].x=w[i],qu[qt].op=-1,qu[qt].id=i;
		qu[++qt].l=dfn[i]+sz[i]-1,qu[qt].x=w[i],qu[qt].op=1,qu[qt].id=i;
	}
	sort(qu+1,qu+1+qt,cmp);
	T[0].init();
	for(int i=1,j=1;i<=n;++i){
    
    
		T[0].add(w[q[i]],1);
		while(j<=qt&&qu[j].l<=i){
    
    
			b[qu[j].id]+=qu[j].op*(T[0].get(inf)-T[0].get(qu[j].x));
			++j;
		}
	}
	for(int i=1;i<=n;++i)ans[n]+=a[i];
	for(int i=1;i<=n;++i){
    
    
		Q.push(-(dep[i]-1-a[i]-b[i]-c[i]));
	}
	for(int i=n-1;i+1;--i){
    
    
		ll now=-Q.top();
		ans[i]=ans[i+1]+now;
		Q.pop();
	}
	for(int i=0;i<=n;++i){
    
    
		ans[i]+=1ll*(n-i)*(n-i-1)/2;
		printf("%lld\n",ans[i]);
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/CSDNzhanghongyu/article/details/110500707