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;
}