学习笔记 · 2026-07-31

【luogu题解】CF1387A 图上解方程

一个蒟蒻来水本题第一篇题解

分析

首先不难发现一条边$(u,v,w)$表示的是一个方程 $x_u+x_v=w$ ,那么问题就转换为了方程组是否有解,求出绝对值最小解的问题

实现

首先图是不一定联通的,但因为每个连通块是独立的,所以可以分开处理。对于一个连通块如果存在唯一解,就把这个唯一解求出来,具体可以使用。

对于一个连通块存在多解,那么不妨设其中一个点的值为 $x$ 然后用 $±x+b$ 的方式把其他点值表示出来。然后再回到这个连通块对于答案的贡献:

$$\sum_{i=1}^{siz} \left\vert x-b_i\right\vert$$

考虑绝对值之和的几何意义,可以把每个 $\left\vert x-b_i\right\vert$ 放到数轴上,显然让 $x$ 等于最中间的那一段(或者最中间的那一个点)的时候贡献最小,那么就排个序求出中间值即可

最后不要忘记用已知的那个点的值把其他点值推出来,整个算法的过程可以使用 pair<int,int>来实现。

代码

#include <bits/stdc++.h>
#define pii pair<int,int> 
#define mpr make_pair
using namespace std;
inline int read(){
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch))f^=(ch=='-'),ch=getchar();
	while(isdigit(ch))x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
	return f?x:-x;
}
const int N=1e5+5,M=2e5+5;
const double eps=1e-6;
struct edge{
	int to,p,w;
}d[M*2];
int last[N],cnt,n,m,vis[N],calc[N],sta[N],stop,mvp[N];
double ans[N];
pii coe[N];
map<pii,int> use;
void charu(int x,int y,int z){
	d[++cnt]=edge{y,last[x],z};
	last[x]=cnt;
}
void solve(int s){
	coe[s]={1,0};
	queue<int> que;
	vis[s]=1;
	que.push(s);
	stop=0;
	while(que.size()){
		int u=que.front();
		sta[++stop]=u;
		que.pop();
		for(int e=last[u];e;e=d[e].p){
			int v=d[e].to,w=d[e].w;
			pii ev={-coe[u].first,w-coe[u].second};
			if(vis[v]){
				if(ev==coe[v])continue;
				if(ev.first==coe[v].first)exit(puts("NO")&0);
				double uu;
				uu=1.00*(coe[v].second-ev.second)/(1.00*(ev.first-coe[v].first));
				if(!calc[s])ans[s]=uu,calc[s]=1;
				else if(fabs(ans[s]-uu)>eps)exit(puts("NO")&0);
			}
			else{
				vis[v]=1;
				coe[v]=ev;
				que.push(v);
			}
		}
	}
	if(!calc[s]){
		for(int i=1;i<=stop;i++)
			mvp[i]=-coe[sta[i]].second/coe[sta[i]].first;
		sort(mvp+1,mvp+stop+1);
		ans[s]=mvp[(stop+1)/2];calc[s]=1;
	}
	for(int i=2;i<=stop;i++){
		ans[sta[i]]=coe[sta[i]].first*ans[s]+coe[sta[i]].second;
	}
}
signed main(){
	n=read(),m=read();
	for(int i=1;i<=m;i++){
		int x=read(),y=read(),z=read();
		if(use[mpr(x,y)]){
			if(use[mpr(x,y)]!=z)
				exit(0&puts("NO"));
			continue;
		}
		use[mpr(x,y)]=use[mpr(y,x)]=z;
		charu(x,y,z),charu(y,x,z);
	}
	for(int i=1;i<=n;i++)if(!vis[i])solve(i);
	puts("YES");
	for(int i=1;i<=n;i++)printf("%lf ",ans[i]);
}

事实证明,码力很重要的