EZEC-6造树

题目链接:「EZEC-6」造树

题意简述:给一张完全图,每个点有权值\(b_i\),边\((u,v)\)的权值定义为\(b_u * b_v\),求满足\(deg_u = a_u\)的最大生成树(保证存在).

\(a_u,b_u \leq 5e5,n \leq 1e7\)

  • 神仙贪心
  • 以下度数都是指当前状态下的度数,而非初始时
  • 考虑答案是\(\sum b_u * b_v\)的形式,根据著名的排序不等式,我们尽可能让大的与大的连.
  • 于是有以下贪心策略,将\(b_u\)从大到小排序.从前往后扫,每次尽可能让\(b_u\)匹配到最大的(在保证能生成树的前提下)
  • 我们考虑维护一个\(l\),表示[1~l]已经形成了连通块,那么显然只要这个连通块还能向外连的度数不为0,那么就合法.
  • 于是我们考虑维护这个连通块.需要一些分类讨论
  • 首先,若\(deg_u\)仍然\(> 1\)那么我们只要\(deg_l > 0\)直接连,否则\(l++\).
  • 否则分两种情况.
  • 若[i ~ l]还存在点有度数,即该连通块即使\(i\)连完了,度数也不为\(0\),那么直接连
  • 否则,我们要一直跳到第一个度数不为\(1\)的点\(k\),然后批量处理\([l,k]\)内的值,注意到\([i~l)\)一定是\(1000....0\),\([l,k]\)一定是\(11111...111x,(x \geq 1)\).那么我们考虑一直把这些\(1\)\(x\)上连,直到\(x\)为1,或者\(1\)被连完.
  • 具体地,我们除了维护\(l,k\)外,还需维护一个标记在连通块内最右边的度数不为\(0\)的点.用来判断,\([i,l]\)内是否还存在点有度数
  • 代码细节很多.
/*EZEC-6造树*/
#include<bits/stdc++.h>
using namespace std;
#define ll long long
int read(){
	char c = getchar();
	int x = 0;
	while(c < '0' || c > '9')		c = getchar();
	while(c >= '0' && c <= '9')		x = x * 10 + c - 48,c = getchar();
	return x;
}
const int N = 1e7 + 7;
int n;
unsigned seed;
struct O{
	int a,b;
}t[N],dat[N];
unsigned rnd(unsigned x){
	x^=x<<13;
	x^=x>>17;
	x^=x<<5;
	return x;
}
int rad(int x,int y){
	seed=rnd(seed);
	return seed%(y-x+1)+x;
}
void init_data(){
	cin>>seed;
	for(int i=1;i<=n;i++)
		t[i].a=1,t[i].b=rad(1,500000);
	for(int i=1;i<=n-2;i++)
		t[rad(1,n)].a++;
}
typedef pair<int,int> pii;
priority_queue<pii>q1,q2;
#define mp make_pair
#define se second
bool cmp(O a,O b){
	return a.b > b.b;
}
bool sign[N];
int main(){
//	freopen("tree.in","r",stdin);
//	freopen("tree.out","w",stdout);
	int type = read();n = read();
	if(type == 0){
		for(int i = 1; i <= n; ++i)	t[i].a = read();
		for(int i = 1; i <= n; ++i)	t[i].b = read();
	}
	else	init_data();
	sort(t+1,t+n+1,cmp);
	ll ans = 0;
	int l = 2;
	int far = 0;/*记录连通块最右边有值的地方*/ 
	for(int i = 1,k = 2; i <= n; ++i){
		l = max(l,i+1);
		if(t[i].a == 0)	continue;
		while(t[i].a > 1){
			while(t[l].a == 0 && l + 1 <= n)	l++;
			t[l].a--;t[i].a--;ans += 1ll * t[i].b * t[l].b;
			if(t[l].a > 0)	far = l;
			l++;
		}
		if(far > i){
			if(t[i].a == 1){
				while(t[l].a == 0 && l + 1 <= n)	l++;
				if(t[l].a > 1)	far = l;
				t[l].a--;t[i].a--;ans += 1ll * t[i].b * t[l].b;l++;
			}
		}
		else{/*单独处理这一整段*/
			k = max(k,l);while(t[k].a <= 1 && k < n)	k++;
			ans += 1ll * t[i].b * t[k].b;t[i].a--,t[k].a--;i = l;
			while(i < k && t[k].a > 1){
				ans += 1ll * t[i].b * t[k].b,t[i].a--,t[k].a--;i++;
			}
			if(t[k].a == 1)	far = i;else	far = k;i--;
		}
	}
	cout<<ans<<endl;
	return 0;
}
posted @ 2021-03-17 22:14  y_dove  阅读(148)  评论(0编辑  收藏  举报