专栏文章

题解:P10768 「CROI · R2」落月摇情

P10768题解参与者 1已保存评论 0

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
0 条
当前快照
1 份
快照标识符
@min19g4q
此快照首次捕获于
2025/12/01 18:55
3 个月前
此快照最后确认于
2025/12/01 18:55
3 个月前
查看原文
昨夜闲潭梦落花,可怜春半不还家。
江水流春去欲尽,江潭落月复西斜。

Solution P10768

Problem 1
mm 条边的构成是什么?
Solution to Problem 1
首先,mm 条边一定是:原图的 MST 与剩下的边里面选 m(n1)m-(n-1) 条。
证明是显然的:你要保证所有点都联通,所以你选择一个 MST。现在联通了,那你贪心的选择最小的加进去就行。
Problem 2
如何求 MST?
Solution to Problem 2
不难发现:
  • 如果 ai<0a_i<0,则你给他连上 aia_i 的最大值位置最佳;
  • 如果 ai>0a_i>0,则你给他连上 aia_i 的最小值最佳;
  • 如果 ai=0a_i=0,你随便连,归到上面一类解决。
解决了 MST 问题。
Problem 3
如何往上面加非 MST 边?
Solution to Problem 3
很显然可以把所有边都算一遍排个序,但是复杂度 O(n2)\operatorname{O}(n^2),会爆炸。
你可以先对 aa 排序,然后维护一个堆。
pip_i 为排序之后对应位置为 ii 的点。
那么你会发现:如果某个点 kk 权值 >0>0,则如果 (p1,k)(p_1,k) 还没有被计算,(p2,k)(p_2,k) 进入这个堆是无意义的,因为 w(p2,k)>w(p1,k)w_{(p_2,k)}>w_{(p_1,k)},我们在算到 (p1,k)(p_1,k) 的时候再把 (p2,k)(p_2,k) 塞进去就行了。
同理,小于 00 的时候,就是从 (pn,k)(p_n,k) 开始处理,如果处理到了就把 (pn1,k)(p_{n-1},k) 塞进去。
这样你只会处理 mm 次加边操作,复杂度 O(mlogn)\operatorname{O}(m\log n)
小问题
两步中,MST 中的边可能会被第二步也处理到。加上 MST 中的边也可能发生重复(例如 a={1,1}a=\{-1,1\},那么处理 11 时会处理 (1,2)(1,2),处理 22 时处理的还是 (1,2)(1,2),所以如何判重边?
Solution
最简单的想法是拿一个 map 存一下这条边是不是被加了,但是常数太大加之带 log\log T 飞了。
于是改用 gp_hash_table,这是一个 pb_ds 库里的东西,在 CCF 系列竞赛中可以使用。在日常调试中,需要:
CPP
#include<bits/extc++.h>
using namespace __gnu_pbds;
AC CodeCPP
#include<bits/stdc++.h>
#define ll long long
#define fi first
#define se second
#define fastio ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
#define ls (now<<1)
#define rs ((now<<1)|1)
#include<bits/extc++.h>
using namespace std;
using namespace __gnu_pbds;
const int N=1000005;
const int mod=1;
const int intinf=0x3f3f3f3f;
const ll llinf=0x3f3f3f3f3f3f3f3f;
struct node{
	ll w;
	int u,v;
	friend bool operator <(node _,node __){
		return _.w>__.w;
	}
};
node make_node(int u,int v,ll w){
	node rrat;
	rrat.u=u;
	rrat.v=v;
	rrat.w=w;
	return rrat;
}
gp_hash_table<int,int>mp[N];
std::priority_queue<node>q;
struct Node{
	int a,id;
	friend bool operator <(Node _,Node __){
		return _.a<__.a;
	}
}A[N];
ll a[N];int id[N];
int n,m;
ll sum=0;
vector<pair<int,int> >ans;
void add(int u,int v){
	if(mp[u][v]||u==v)return;
	ans.push_back(make_pair(u,v));
	mp[u][v]=1;mp[v][u]=1;
	sum+=a[u]*a[v];
} 
signed main(){
	fastio;
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		cin>>A[i].a;A[i].id=i;
	}
	sort(A+1,A+n+1);
	for(int i=1;i<=n;i++){
		a[i]=A[i].a;
		id[i]=A[i].id;
	} 
	for(int i=1;i<=n;i++){
		if(a[i]<0){
			add(i,n);
			q.push(make_node(i,n,a[i]*a[n]));
		}
		else {
			add(i,1);
			q.push(make_node(i,1,a[i]*a[1]));
		}
	}
	int cnt=n-1;
	while(!q.empty()&&cnt<m){
		node tmp=q.top();q.pop();
		int u=tmp.u,v=tmp.v;
		if(a[u]>=0){
			if(v!=n)q.push(make_node(u,v+1,a[u]*a[v+1]));
		}
		else{
			if(v!=1){
				q.push(make_node(u,v-1,a[u]*a[v-1]));
			}
		}
		if(mp[u][v])continue;
		if(u==v)continue;
		cnt++;
		add(u,v);
	}
	cout<<sum<<'\n';
	for(auto p:ans){
		cout<<id[p.fi]<<' '<<id[p.se]<<'\n';
	} 
	return 0;
}

评论

0 条评论,欢迎与作者交流。

正在加载评论...