【中位数贪心】P6696 [BalticOI 2020] 图 (Day2)|普及+

本文涉及知识点

数学
C++DFS
C++贪心

P6696 [BalticOI 2020] 图 (Day2)

题目描述

你有一个无向图,每条边都有一种颜色:红或者黑。

你要做的就是为每个节点配一个实数点权,使得:

  • 对于每条黑色边,两个端点的点权之和为 1 1 1
  • 对于每条红色边,两个端点的点权之和为 2 2 2
  • 所有点权的绝对值之和是最小的

求一种点权的分配方案。

输入格式

第一行两个整数 N , M N,M N,M 代表点数和边数。

所有点的编号为 1 1 1 到 N N N。

接下来 M M M 行每行三个整数 a , b , c a,b,c a,b,c 描述一条边端点为 a a a 和 b b b,如果 c c c 是 1 1 1 那么这条边是黑色边,如果 c c c 是 2 2 2 那么这条边是红色边。

输出格式

如果有解,首先第一行输出 YES,然后第二行 N N N 个整数代表可能的一组点权。

多组解输出任意一组即可。

如果无解,一行一个字符串 NO

输入输出样例 #1

输入 #1

复制代码
4 4
1 2 1
2 3 2
1 3 2
3 4 1

输出 #1

复制代码
YES
0.5 0.5 1.5 -0.5

输入输出样例 #2

输入 #2

复制代码
2 1
1 2 1

输出 #2

复制代码
YES
0.3 0.7

输入输出样例 #3

输入 #3

复制代码
3 2
1 2 2
2 3 2

输出 #3

复制代码
YES
0 2 0

输入输出样例 #4

输入 #4

复制代码
3 4
1 2 2
2 2 1
2 1 1
1 2 2

输出 #4

复制代码
NO

说明/提示

评测方式

您的输出被评判为正确,当且仅当:

  • 每条边所连两点的点权和与该边要求的点权间的误差不超过 1 0 − 6 10^{-6} 10−6。
  • 所有点权的绝对值之和与标准答案误差不超过 1 0 − 6 10^{-6} 10−6。
数据规模与约定

本题采用捆绑测试。

  • Subtask 1(5 pts): N ≤ 5 N \le 5 N≤5, M ≤ 14 M \le 14 M≤14。
  • Subtask 2(12 pts): N ≤ 100 N \le 100 N≤100。
  • Subtask 3(17 pts): N ≤ 1000 N \le 1000 N≤1000。
  • Subtask 4(24 pts): N ≤ 1 0 4 N \le 10^4 N≤104。
  • Subtask 5(42 pts):无特殊限制。

对于 100 % 100\% 100% 的数据, 1 ≤ N ≤ 1 0 5 1 \le N \le 10^5 1≤N≤105, 0 ≤ M ≤ 2 × 1 0 5 0 \le M \le 2 \times 10^5 0≤M≤2×105。

本题使用 Special Judge。

[BalticOI 2020] 图 (Day2) DFS|普及+

注意 :本题有自环和重边,如样例4。自环可以按奇数环处理。重边的权重必须同。可排序后比较。
性质一 :不同的连通区域互不影响。各连通区域要分别处理。

本题等效于,黑色边之和2,红边之和4,最终结果除以2。

如果没有环,任意节点为x,然后计算其它节点的点权。下面讨论环:

长度为3的奇数环,A的点权是x1,AB、BC、CA的边权分别为ew1,ew2,ew2。x2 = ew1-x1。x3=ew2-(ew1-x1)=x1+ew2-ew1。x3+x1=ew3。即:

2x1= ew3-ew2+ew1。即: x = (ew3+ew1-ew2)/2。奇数环类似。

长度为4的偶数环:x4=ew3-x3 = ew3-ew2+ew1-x1。x4+x1 =ew4,即ew4-ew3+ew2-ew1=0。
结论一:偶数环,边权之和必须为0。奇数环可以确定。

DFS

DFS时,任意节点只在第一次DFS时访问临接点,即每条边只访问一次。故时间复杂度:O(n+m)。

DFS(cur, vGP,is) cur是当前节点,vGP是cur的祖先,vGP.back()是cur的父节点。vw[i]记录gp[i]到父节点的权, is[i] 记录i是否是cur的祖先。如果is[cur]成立,说明遇到环(不是第一次访问), 不访问临接点。

如果是偶数环,判断边权是否是0,如果不是0,直接结束程序,无解。如果是奇数环,求出cur的点权。每个环只会访问两次,cur是第一个DFS到的点,访问两次是无向边。如果有奇数环,相互矛盾则某个节点两次计算的值不相同。

BFS

如果某个连通区域有奇数环,则直接计算各点权。如果有连通区域没有奇数环,则任意一点权为x,其它点可以kx+b, k是1或-1。|x+b|等效于|x-(-b)|,|-x+b|等效于|x-b| 。我们要想各点权绝对值之和最小,就是x到bs各点距离和最小。 x取bs的中位数。如果b有偶数中则中间任意一个。k为1,bs增加-b;k为-1,bs增加b。求出x后,计算本连通区域各点的值。

代码:每个测试点用少量样例过不了,过于复杂很难排查

cpp 复制代码
#include <iostream>
#include <sstream>
#include <vector>
#include<map>
#include<unordered_map>
#include<set>
#include<unordered_set>
#include<string>
#include<algorithm>
#include<functional>
#include<queue>
#include <stack>
#include<iomanip>
#include<numeric>
#include <math.h>
#include <climits>
#include<assert.h>
#include<cstring>
#include<list>

#include <bitset>
using namespace std;

template<class T1, class T2>
std::istream& operator >> (std::istream& in, pair<T1, T2>& pr) {
	in >> pr.first >> pr.second;
	return in;
}

template<class T1, class T2, class T3 >
std::istream& operator >> (std::istream& in, tuple<T1, T2, T3>& t) {
	in >> get<0>(t) >> get<1>(t) >> get<2>(t);
	return in;
}

template<class T1, class T2, class T3, class T4 >
std::istream& operator >> (std::istream& in, tuple<T1, T2, T3, T4>& t) {
	in >> get<0>(t) >> get<1>(t) >> get<2>(t) >> get<3>(t);
	return in;
}

template<class T = int>
vector<T> Read() {
	int n;
	cin >> n;
	vector<T> ret(n);
	for (int i = 0; i < n; i++) {
		cin >> ret[i];
	}
	return ret;
}
template<class T = int>
vector<T> ReadNotNum() {
	vector<T> ret;
	T tmp;
	while (cin >> tmp) {
		ret.emplace_back(tmp);
		if ('\n' == cin.get()) { break; }
	}
	return ret;
}

template<class T = int>
vector<T> Read(int n) {
	vector<T> ret(n);
	for (int i = 0; i < n; i++) {
		cin >> ret[i];
	}
	return ret;
}

template<int N = 1'000'000>
class COutBuff
{
public:
	COutBuff() {
		m_p = puffer;
	}
	template<class T>
	void write(T x) {
		int num[28], sp = 0;
		if (x < 0)
			*m_p++ = '-', x = -x;

		if (!x)
			*m_p++ = 48;

		while (x)
			num[++sp] = x % 10, x /= 10;

		while (sp)
			*m_p++ = num[sp--] + 48;
		AuotToFile();
	}
	void writestr(const char* sz) {
		strcpy(m_p, sz);
		m_p += strlen(sz);
		AuotToFile();
	}
	inline void write(char ch)
	{
		*m_p++ = ch;
		AuotToFile();
	}
	inline void ToFile() {
		fwrite(puffer, 1, m_p - puffer, stdout);
		m_p = puffer;
	}
	~COutBuff() {
		ToFile();
	}
private:
	inline void AuotToFile() {
		if (m_p - puffer > N - 100) {
			ToFile();
		}
	}
	char  puffer[N], * m_p;
};

template<int N = 1'000'000>
class CInBuff
{
public:
	inline CInBuff() {}
	inline CInBuff<N>& operator>>(char& ch) {
		FileToBuf();
		ch = *S++;
		return *this;
	}
	inline CInBuff<N>& operator>>(int& val) {
		FileToBuf();
		int x(0), f(0);
		while (!isdigit(*S))
			f |= (*S++ == '-');
		while (isdigit(*S))
			x = (x << 1) + (x << 3) + (*S++ ^ 48);
		val = f ? -x : x; S++;//忽略空格换行		
		return *this;
	}
	inline CInBuff& operator>>(long long& val) {
		FileToBuf();
		long long x(0); int f(0);
		while (!isdigit(*S))
			f |= (*S++ == '-');
		while (isdigit(*S))
			x = (x << 1) + (x << 3) + (*S++ ^ 48);
		val = f ? -x : x; S++;//忽略空格换行
		return *this;
	}
	template<class T1, class T2>
	inline CInBuff& operator>>(pair<T1, T2>& val) {
		*this >> val.first >> val.second;
		return *this;
	}
	template<class T1, class T2, class T3>
	inline CInBuff& operator>>(tuple<T1, T2, T3>& val) {
		*this >> get<0>(val) >> get<1>(val) >> get<2>(val);
		return *this;
	}
	template<class T1, class T2, class T3, class T4>
	inline CInBuff& operator>>(tuple<T1, T2, T3, T4>& val) {
		*this >> get<0>(val) >> get<1>(val) >> get<2>(val) >> get<3>(val);
		return *this;
	}
	template<class T = int>
	inline CInBuff& operator>>(vector<T>& val) {
		int n;
		*this >> n;
		val.resize(n);
		for (int i = 0; i < n; i++) {
			*this >> val[i];
		}
		return *this;
	}
	template<class T = int>
	vector<T> Read(int n) {
		vector<T> ret(n);
		for (int i = 0; i < n; i++) {
			*this >> ret[i];
		}
		return ret;
	}
	template<class T = int>
	vector<T> Read() {
		vector<T> ret;
		*this >> ret;
		return ret;
	}
private:
	inline void FileToBuf() {
		const int canRead = m_iWritePos - (S - buffer);
		if (canRead >= 100) { return; }
		if (m_bFinish) { return; }
		for (int i = 0; i < canRead; i++)
		{
			buffer[i] = S[i];//memcpy出错			
		}
		m_iWritePos = canRead;
		buffer[m_iWritePos] = 0;
		S = buffer;
		int readCnt = fread(buffer + m_iWritePos, 1, N - m_iWritePos, stdin);
		if (readCnt <= 0) { m_bFinish = true; return; }
		m_iWritePos += readCnt;
		buffer[m_iWritePos] = 0;
		S = buffer;
	}
	int m_iWritePos = 0; bool m_bFinish = false;
	char buffer[N + 10], * S = buffer;
};


class KMP
{
public:
	virtual int Find(const string& s, const string& t)
	{
		CalLen(t);
		for (int i1 = 0, j = 0; i1 < s.length(); )
		{
			for (; (j < t.length()) && (i1 + j < s.length()) && (s[i1 + j] == t[j]); j++);
			//i2 = i1 + j 此时s[i1,i2)和t[0,j)相等 s[i2]和t[j]不存在或相等
			//t[0,j)的结尾索引是j-1,所以最长公共前缀为m_vLen[j-1],简写为y 则t[0,y)等于t[j-y,j)等于s[i2-y,i2)
			if (0 == j)
			{
				i1++;
				continue;
			}
			const int i2 = i1 + j;
			j = m_vLen[j - 1];
			i1 = i2 - j;//i2不变
		}
		return -1;
	}
	//vector<int> m_vSameLen;//m_vSame[i]记录 s[i...]和t[0...]最长公共前缀,增加可调试性 部分m_vSameLen[i]会缺失
	//static vector<int> Next(const string& s)
	//{// j = vNext[i] 表示s[0,i]的最大公共前后缀是s[0,j]
	//	const int len = s.length();
	//	vector<int> vNext(len, -1);
	//	for (int i = 1; i < len; i++)
	//	{
	//		int next = vNext[i - 1];
	//		while ((-1 != next) && (s[next + 1] != s[i]))
	//		{
	//			next = vNext[next];
	//		}
	//		vNext[i] = next + (s[next + 1] == s[i]);
	//	}
	//	return vNext;
	//}

	const vector<int> CalLen(const string& str)
	{
		m_vLen.resize(str.length());
		for (int i = 1; i < str.length(); i++)
		{
			int next = m_vLen[i - 1];
			while (str[next] != str[i])
			{
				if (0 == next)
				{
					break;
				}
				next = m_vLen[next - 1];
			}
			m_vLen[i] = next + (str[next] == str[i]);
		}
		return m_vLen;
	}
protected:
	int m_c;
	vector<int> m_vLen;//m_vLen[i] 表示str[0,i]的最长公共前后缀的长度
};

template<long long MOD = 1000000007, class T1 = int, class T2 = long long>
class C1097Int
{
public:
	C1097Int(T1 iData = 0) :m_iData(iData% MOD)
	{

	}
	C1097Int(T2 llData) :m_iData(llData% MOD) {

	}
	C1097Int  operator+(const C1097Int& o)const
	{
		return C1097Int(((T2)m_iData + o.m_iData) % MOD);
	}
	C1097Int& operator+=(const C1097Int& o)
	{
		m_iData = ((T2)m_iData + o.m_iData) % MOD;
		return *this;
	}
	C1097Int& operator-=(const C1097Int& o)
	{
		m_iData = ((T2)MOD + m_iData - o.m_iData) % MOD;
		return *this;
	}
	C1097Int  operator-(const C1097Int& o)
	{
		return C1097Int(((T2)MOD + m_iData - o.m_iData) % MOD);
	}
	C1097Int  operator*(const C1097Int& o)const
	{
		return((T2)m_iData * o.m_iData) % MOD;
	}
	C1097Int& operator*=(const C1097Int& o)
	{
		m_iData = ((T2)m_iData * o.m_iData) % MOD;
		return *this;
	}
	C1097Int  operator/(const C1097Int& o)const
	{
		return *this * o.PowNegative1();
	}
	C1097Int& operator/=(const C1097Int& o)
	{
		*this /= o.PowNegative1();
		return *this;
	}
	bool operator==(const C1097Int& o)const
	{
		return m_iData == o.m_iData;
	}
	bool operator<(const C1097Int& o)const
	{
		return m_iData < o.m_iData;
	}
	C1097Int pow(T2 n)const
	{
		C1097Int iRet = (T1)1, iCur = *this;
		while (n)
		{
			if (n & 1)
			{
				iRet *= iCur;
			}
			iCur *= iCur;
			n >>= 1;
		}
		return iRet;
	}
	C1097Int PowNegative1()const
	{
		return pow(MOD - 2);
	}
	T1 ToInt()const
	{
		return ((T2)m_iData + MOD) % MOD;
	}
private:
	T1 m_iData = 0;;
};

class CParentToNeiBo
{
public:
	CParentToNeiBo(const vector<int>& parents)
	{
		m_vNeiBo.resize(parents.size());
		for (int i = 0; i < parents.size(); i++)
		{
			if (-1 == parents[i])
			{
				m_root = i;
			}
			else
			{
				m_vNeiBo[parents[i]].emplace_back(i);
			}
		}
	}
	vector<vector<int>> m_vNeiBo;
	int m_root = -1;
};
class CBFSLeve {
public:
	static vector<int> Leve(const vector<vector<int>>& neiBo, vector<int> start) {
		vector<int> leves(neiBo.size(), -1);
		for (const auto& s : start) {
			leves[s] = 0;
		}
		for (int i = 0; i < start.size(); i++) {
			for (const auto& next : neiBo[start[i]]) {
				if (-1 != leves[next]) { continue; }
				leves[next] = leves[start[i]] + 1;
				start.emplace_back(next);
			}
		}
		return leves;
	}
	template<class NextFun>
	static vector<int> Leve(int N, NextFun nextFun, vector<int> start) {
		vector<int> leves(N, -1);
		for (const auto& s : start) {
			leves[s] = 0;
		}
		for (int i = 0; i < start.size(); i++) {
			auto nexts = nextFun(start[i]);
			for (const auto& next : nexts) {
				if (-1 != leves[next]) { continue; }
				leves[next] = leves[start[i]] + 1;
				start.emplace_back(next);
			}
		}
		return leves;
	}
	static vector<vector<int>> LeveNodes(const vector<int>& leves) {
		const int iMaxLeve = *max_element(leves.begin(), leves.end());
		vector<vector<int>> ret(iMaxLeve + 1);
		for (int i = 0; i < leves.size(); i++) {
			ret[leves[i]].emplace_back(i);
		}
		return ret;
	};
	static vector<int> LeveSort(const vector<int>& leves) {
		const int iMaxLeve = *max_element(leves.begin(), leves.end());
		vector<vector<int>> leveNodes(iMaxLeve + 1);
		for (int i = 0; i < leves.size(); i++) {
			leveNodes[leves[i]].emplace_back(i);
		}
		vector<int> ret;
		for (const auto& v : leveNodes) {
			ret.insert(ret.end(), v.begin(), v.end());
		}
		return ret;
	};
};

class CParents
{
public:
	CParents(vector<int>& vParent, long long iMaxDepth)
	{
		int iBitNum = 0;
		for (; iMaxDepth; iBitNum++) {
			const auto mask = 1LL << iBitNum;
			if (mask & iMaxDepth) { iMaxDepth = iMaxDepth ^ mask; }
		}
		const int n = vParent.size();
		m_vParents.assign(iBitNum + 1, vector<int>(n, -1));
		m_vParents[0] = vParent;
		//树上倍增
		for (int i = 1; i < m_vParents.size(); i++)
		{
			for (int j = 0; j < n; j++)
			{
				const int iPre = m_vParents[i - 1][j];
				if (-1 != iPre)
				{
					m_vParents[i][j] = m_vParents[i - 1][iPre];
				}
			}
		}
	}
	int GetParent(int iNode, int iDepth)const
	{
		int iParent = iNode;
		for (int iBit = 0; iBit < m_vParents.size(); iBit++)
		{
			if (-1 == iParent)
			{
				return iParent;
			}
			if (iDepth & (1 << iBit))
			{
				iParent = m_vParents[iBit][iParent];
			}
		}
		return iParent;
	}
	inline int GetBitCnt()const { return m_vParents.size(); };
	inline const int& GetPow2Parent(int iNode, int n)const {
		return m_vParents[n][iNode];
	}
	//在leftNodeExclude的1到rightLeve级祖先中查找符合pr的最近祖先
	template<class _Pr>
	int FindFirst(int leftNodeExclude, int rightLeve, _Pr pr) {
		for (int iBit = GetBitCnt() - 1; iBit >= 0; iBit--) {
			const int iMask = 1 << iBit;
			if (!(iMask & rightLeve)) { continue; }
			if (pr(m_vParents[iBit][leftNodeExclude])) {
				return BFindFirst(leftNodeExclude, iBit, pr);
			}
			leftNodeExclude = m_vParents[iBit][leftNodeExclude];
		}
		return -1;
	}
protected:
	//在leftNodeExclude的1到2^pow^级祖先中查找符合pr的最近祖先
	template<class _Pr>
	int BFindFirst(int leftNodeExclude, int pow, _Pr pr) {
		while (pow > 0) {
			const int& mid = m_vParents[pow - 1][leftNodeExclude];
			if (pr(mid)) {
			}
			else {
				leftNodeExclude = mid;
			}
			pow--;
		}
		return m_vParents[0][leftNodeExclude];
	}
	vector<vector<int>> m_vParents;
};

class CNeiBo
{
public:
	static vector<vector<int>> Two(int n, const vector<pair<int, int>>& edges, bool bDirect, int iBase = 0)
	{
		vector<vector<int>>  vNeiBo(n);
		for (const auto& [i1, i2] : edges)
		{
			vNeiBo[i1 - iBase].emplace_back(i2 - iBase);
			if (!bDirect)
			{
				vNeiBo[i2 - iBase].emplace_back(i1 - iBase);
			}
		}
		return vNeiBo;
	}
	static vector<vector<int>> Two(int n, const vector<vector<int>>& edges, bool bDirect, int iBase = 0)
	{
		vector<vector<int>>  vNeiBo(n);
		for (const auto& v : edges)
		{
			vNeiBo[v[0] - iBase].emplace_back(v[1] - iBase);
			if (!bDirect)
			{
				vNeiBo[v[1] - iBase].emplace_back(v[0] - iBase);
			}
		}
		return vNeiBo;
	}
	static vector<vector<std::pair<int, int>>> Three(int n, vector<vector<int>>& edges, bool bDirect, int iBase = 0)
	{
		vector<vector<std::pair<int, int>>> vNeiBo(n);
		for (const auto& v : edges)
		{
			vNeiBo[v[0] - iBase].emplace_back(v[1] - iBase, v[2]);
			if (!bDirect)
			{
				vNeiBo[v[1] - iBase].emplace_back(v[0] - iBase, v[2]);
			}
		}
		return vNeiBo;
	}
	static vector<vector<std::pair<int, int>>> Three(int n, const vector<tuple<int, int, int>>& edges, bool bDirect, int iBase = 0)
	{
		vector<vector<std::pair<int, int>>> vNeiBo(n);
		for (const auto& [u, v, w] : edges)
		{
			vNeiBo[u - iBase].emplace_back(v - iBase, w);
			if (!bDirect)
			{
				vNeiBo[v - iBase].emplace_back(u - iBase, w);
			}
		}
		return vNeiBo;
	}
	static vector<vector<int>> Mat(vector<vector<int>>& neiBoMat)
	{
		vector<vector<int>> neiBo(neiBoMat.size());
		for (int i = 0; i < neiBoMat.size(); i++)
		{
			for (int j = i + 1; j < neiBoMat.size(); j++)
			{
				if (neiBoMat[i][j])
				{
					neiBo[i].emplace_back(j);
					neiBo[j].emplace_back(i);
				}
			}
		}
		return neiBo;
	}
};

class Solution {
public:
	vector<float> Ans(int N, const vector<tuple<int, int, int>>& edge) {
		m_iN = N;
		m_neiBo = CNeiBo::Three(N, edge, false, 1);
		for (auto& v : m_neiBo) {
			for (auto& [next, w] : v) {
				w *= 2;
			}
			sort(v.begin(), v.end());
			for (int i = 1; i < v.size(); i++) {
				if ((v[i].first == v[i - 1].first) && (v[i].second != v[i - 1].second)) { return {}; }
			}
		}

		m_vk.assign(N, -100); m_vb.assign(N, 0);
		for (int i = 0; i < N; i++)
		{
			if (-100 != m_vk[i]) { continue; }
			int ring = -1;
			if (!DFS(ring, i)) {
				return {};
			}
			if (-1 == ring) {
				ring = i; m_vk[ring] = 1;
			}
			BFS(ring);
		}
		vector<float> ans;
		for (int i = 0; i < N; i++) {
			ans.emplace_back(m_vb[i] / 2.0);
		}
		return ans;
	}
	bool DFS(int& ring, int cur) {
		vector<int> gp, vw; vector<bool> is(m_iN);
		function<bool(int, int)> DFS = [&](int cur, int w) {
			if (is[cur]) {//遇到环
				ring = cur;
				int sign = -1, sum = w;
				for (int i = gp.size() - 1; cur != gp[i]; i--) {
					sum += sign * vw[i];
					sign = (1 == sign) ? -1 : 1;
				}
				if ((1 == sign) && (sum != 0)) {
					return false;
				}
				if (-1 == sign) {
					if ((-100 != m_vk[cur]) && (2 * m_vb[cur] != sum)) {
						return false;
					}
					m_vk[cur] = 0; m_vb[cur] = sum / 2;
				}
				return true;
			}
			gp.emplace_back(cur); vw.emplace_back(w); is[cur] = true;
			for (const auto& [next, w] : m_neiBo[cur]) {
				if ((gp.size() >= 2) && (gp[gp.size() - 2] == next)) { continue; }
				if (!DFS(next, w)) { return false; }
			}
			gp.pop_back(); vw.pop_back(); is[cur] = false;
			return true;
		};
		return DFS(cur, 0);
	}
	void BFS(int root) {
		queue<int> que; vector<bool> vis(m_iN);
		vector<pair<int, int>> bsNode;
		que.emplace(root); vis[root] = true;
		while (que.size()) {
			auto cur = que.front(); que.pop();
			if (1 == m_vk[cur]) {
				bsNode.emplace_back(-m_vb[cur], cur);
			}
			else if (-1 == m_vk[cur]) {
				bsNode.emplace_back(m_vb[cur], cur);
			}
			for (const auto& [next, w] : m_neiBo[cur]) {
				m_vk[next] = -m_vk[cur];
				m_vb[next] = w - m_vb[cur];
				if (!vis[next]) {
					que.emplace(next); vis[next] = true;
				}
			}
		}
		if (bsNode.empty()) { return; }
		nth_element(bsNode.begin(), bsNode.begin() + bsNode.size() / 2, bsNode.end());
		const auto x = bsNode[bsNode.size() / 2].first;
		for (const auto& [tmp, cur] : bsNode) {
			m_vb[cur] += m_vk[cur] * x;
		}
	}
	int m_iN;
	vector<int> m_vk, m_vb;
	vector<vector<std::pair<int, int>>> m_neiBo;
};

int main() {
#ifdef _DEBUG
	freopen("a.in", "r", stdin);
#endif // DEBUG	
	ios::sync_with_stdio(0); cin.tie(nullptr);
	int n;
	cin >> n;
	auto edge = Read<tuple<int, int, int>>();
	
#ifdef _DEBUG		
	printf("N=%d",n);
	//cout << ",s=" << s;
	Out(edge, ",edge=");
	//Out(ws, ",hs=");
	//Out(que, ",que=");
	/*Out(que, "que=");*/
#endif // DEBUG		
	auto res = Solution().Ans(n,edge);
	cout << (res.size()?"YES":"NO" )<< '\n';
	for (const auto& f : res) {
		cout << f << " ";
	}
	return 0;
}

简化

每个连通块,直接令第一个是x,计算其它点。

如果每个点第一次时k1x+b1,第二次是k2x+b2。

如果: k 1 = = k 2 , b 1 = = b 2 k1 == k2,b1 == b2 k1==k2,b1==b2, 忽略。

如果: k 1 = = k 2 , b 1 ≠ b 2 k1 == k2, b1 \neq b2 k1==k2,b1=b2 ,出错。

如果: k 1 ≠ k 2 k1 \neq k2 k1=k2 ,则 x = b 2 − b 1 k 1 − k 2 x= \frac{b2-b1}{k1-k2} x=k1−k2b2−b1

如果没有计算出x,则通同过中位数贪心计算x。

最后将计算的结果演算判断是否无解。中间可以不判断非法,最后统一判断。

核心代码

cpp 复制代码
#include <iostream>
#include <sstream>
#include <vector>
#include<map>
#include<unordered_map>
#include<set>
#include<unordered_set>
#include<string>
#include<algorithm>
#include<functional>
#include<queue>
#include <stack>
#include<iomanip>
#include<numeric>
#include <math.h>
#include <climits>
#include<assert.h>
#include<cstring>
#include<list>

#include <bitset>
using namespace std;

template<class T1, class T2>
std::istream& operator >> (std::istream& in, pair<T1, T2>& pr) {
	in >> pr.first >> pr.second;
	return in;
}

template<class T1, class T2, class T3 >
std::istream& operator >> (std::istream& in, tuple<T1, T2, T3>& t) {
	in >> get<0>(t) >> get<1>(t) >> get<2>(t);
	return in;
}

template<class T1, class T2, class T3, class T4 >
std::istream& operator >> (std::istream& in, tuple<T1, T2, T3, T4>& t) {
	in >> get<0>(t) >> get<1>(t) >> get<2>(t) >> get<3>(t);
	return in;
}

template<class T = int>
vector<T> Read() {
	int n;
	cin >> n;
	vector<T> ret(n);
	for (int i = 0; i < n; i++) {
		cin >> ret[i];
	}
	return ret;
}
template<class T = int>
vector<T> ReadNotNum() {
	vector<T> ret;
	T tmp;
	while (cin >> tmp) {
		ret.emplace_back(tmp);
		if ('\n' == cin.get()) { break; }
	}
	return ret;
}

template<class T = int>
vector<T> Read(int n) {
	vector<T> ret(n);
	for (int i = 0; i < n; i++) {
		cin >> ret[i];
	}
	return ret;
}

template<int N = 1'000'000>
class COutBuff
{
public:
	COutBuff() {
		m_p = puffer;
	}
	template<class T>
	void write(T x) {
		int num[28], sp = 0;
		if (x < 0)
			*m_p++ = '-', x = -x;

		if (!x)
			*m_p++ = 48;

		while (x)
			num[++sp] = x % 10, x /= 10;

		while (sp)
			*m_p++ = num[sp--] + 48;
		AuotToFile();
	}
	void writestr(const char* sz) {
		strcpy(m_p, sz);
		m_p += strlen(sz);
		AuotToFile();
	}
	inline void write(char ch)
	{
		*m_p++ = ch;
		AuotToFile();
	}
	inline void ToFile() {
		fwrite(puffer, 1, m_p - puffer, stdout);
		m_p = puffer;
	}
	~COutBuff() {
		ToFile();
	}
private:
	inline void AuotToFile() {
		if (m_p - puffer > N - 100) {
			ToFile();
		}
	}
	char  puffer[N], * m_p;
};

template<int N = 1'000'000>
class CInBuff
{
public:
	inline CInBuff() {}
	inline CInBuff<N>& operator>>(char& ch) {
		FileToBuf();
		ch = *S++;
		return *this;
	}
	inline CInBuff<N>& operator>>(int& val) {
		FileToBuf();
		int x(0), f(0);
		while (!isdigit(*S))
			f |= (*S++ == '-');
		while (isdigit(*S))
			x = (x << 1) + (x << 3) + (*S++ ^ 48);
		val = f ? -x : x; S++;//忽略空格换行		
		return *this;
	}
	inline CInBuff& operator>>(long long& val) {
		FileToBuf();
		long long x(0); int f(0);
		while (!isdigit(*S))
			f |= (*S++ == '-');
		while (isdigit(*S))
			x = (x << 1) + (x << 3) + (*S++ ^ 48);
		val = f ? -x : x; S++;//忽略空格换行
		return *this;
	}
	template<class T1, class T2>
	inline CInBuff& operator>>(pair<T1, T2>& val) {
		*this >> val.first >> val.second;
		return *this;
	}
	template<class T1, class T2, class T3>
	inline CInBuff& operator>>(tuple<T1, T2, T3>& val) {
		*this >> get<0>(val) >> get<1>(val) >> get<2>(val);
		return *this;
	}
	template<class T1, class T2, class T3, class T4>
	inline CInBuff& operator>>(tuple<T1, T2, T3, T4>& val) {
		*this >> get<0>(val) >> get<1>(val) >> get<2>(val) >> get<3>(val);
		return *this;
	}
	template<class T = int>
	inline CInBuff& operator>>(vector<T>& val) {
		int n;
		*this >> n;
		val.resize(n);
		for (int i = 0; i < n; i++) {
			*this >> val[i];
		}
		return *this;
	}
	template<class T = int>
	vector<T> Read(int n) {
		vector<T> ret(n);
		for (int i = 0; i < n; i++) {
			*this >> ret[i];
		}
		return ret;
	}
	template<class T = int>
	vector<T> Read() {
		vector<T> ret;
		*this >> ret;
		return ret;
	}
private:
	inline void FileToBuf() {
		const int canRead = m_iWritePos - (S - buffer);
		if (canRead >= 100) { return; }
		if (m_bFinish) { return; }
		for (int i = 0; i < canRead; i++)
		{
			buffer[i] = S[i];//memcpy出错			
		}
		m_iWritePos = canRead;
		buffer[m_iWritePos] = 0;
		S = buffer;
		int readCnt = fread(buffer + m_iWritePos, 1, N - m_iWritePos, stdin);
		if (readCnt <= 0) { m_bFinish = true; return; }
		m_iWritePos += readCnt;
		buffer[m_iWritePos] = 0;
		S = buffer;
	}
	int m_iWritePos = 0; bool m_bFinish = false;
	char buffer[N + 10], * S = buffer;
};


class KMP
{
public:
	virtual int Find(const string& s, const string& t)
	{
		CalLen(t);
		for (int i1 = 0, j = 0; i1 < s.length(); )
		{
			for (; (j < t.length()) && (i1 + j < s.length()) && (s[i1 + j] == t[j]); j++);
			//i2 = i1 + j 此时s[i1,i2)和t[0,j)相等 s[i2]和t[j]不存在或相等
			//t[0,j)的结尾索引是j-1,所以最长公共前缀为m_vLen[j-1],简写为y 则t[0,y)等于t[j-y,j)等于s[i2-y,i2)
			if (0 == j)
			{
				i1++;
				continue;
			}
			const int i2 = i1 + j;
			j = m_vLen[j - 1];
			i1 = i2 - j;//i2不变
		}
		return -1;
	}
	//vector<int> m_vSameLen;//m_vSame[i]记录 s[i...]和t[0...]最长公共前缀,增加可调试性 部分m_vSameLen[i]会缺失
	//static vector<int> Next(const string& s)
	//{// j = vNext[i] 表示s[0,i]的最大公共前后缀是s[0,j]
	//	const int len = s.length();
	//	vector<int> vNext(len, -1);
	//	for (int i = 1; i < len; i++)
	//	{
	//		int next = vNext[i - 1];
	//		while ((-1 != next) && (s[next + 1] != s[i]))
	//		{
	//			next = vNext[next];
	//		}
	//		vNext[i] = next + (s[next + 1] == s[i]);
	//	}
	//	return vNext;
	//}

	const vector<int> CalLen(const string& str)
	{
		m_vLen.resize(str.length());
		for (int i = 1; i < str.length(); i++)
		{
			int next = m_vLen[i - 1];
			while (str[next] != str[i])
			{
				if (0 == next)
				{
					break;
				}
				next = m_vLen[next - 1];
			}
			m_vLen[i] = next + (str[next] == str[i]);
		}
		return m_vLen;
	}
protected:
	int m_c;
	vector<int> m_vLen;//m_vLen[i] 表示str[0,i]的最长公共前后缀的长度
};

template<long long MOD = 1000000007, class T1 = int, class T2 = long long>
class C1097Int
{
public:
	C1097Int(T1 iData = 0) :m_iData(iData% MOD)
	{

	}
	C1097Int(T2 llData) :m_iData(llData% MOD) {

	}
	C1097Int  operator+(const C1097Int& o)const
	{
		return C1097Int(((T2)m_iData + o.m_iData) % MOD);
	}
	C1097Int& operator+=(const C1097Int& o)
	{
		m_iData = ((T2)m_iData + o.m_iData) % MOD;
		return *this;
	}
	C1097Int& operator-=(const C1097Int& o)
	{
		m_iData = ((T2)MOD + m_iData - o.m_iData) % MOD;
		return *this;
	}
	C1097Int  operator-(const C1097Int& o)
	{
		return C1097Int(((T2)MOD + m_iData - o.m_iData) % MOD);
	}
	C1097Int  operator*(const C1097Int& o)const
	{
		return((T2)m_iData * o.m_iData) % MOD;
	}
	C1097Int& operator*=(const C1097Int& o)
	{
		m_iData = ((T2)m_iData * o.m_iData) % MOD;
		return *this;
	}
	C1097Int  operator/(const C1097Int& o)const
	{
		return *this * o.PowNegative1();
	}
	C1097Int& operator/=(const C1097Int& o)
	{
		*this /= o.PowNegative1();
		return *this;
	}
	bool operator==(const C1097Int& o)const
	{
		return m_iData == o.m_iData;
	}
	bool operator<(const C1097Int& o)const
	{
		return m_iData < o.m_iData;
	}
	C1097Int pow(T2 n)const
	{
		C1097Int iRet = (T1)1, iCur = *this;
		while (n)
		{
			if (n & 1)
			{
				iRet *= iCur;
			}
			iCur *= iCur;
			n >>= 1;
		}
		return iRet;
	}
	C1097Int PowNegative1()const
	{
		return pow(MOD - 2);
	}
	T1 ToInt()const
	{
		return ((T2)m_iData + MOD) % MOD;
	}
private:
	T1 m_iData = 0;;
};

class CParentToNeiBo
{
public:
	CParentToNeiBo(const vector<int>& parents)
	{
		m_vNeiBo.resize(parents.size());
		for (int i = 0; i < parents.size(); i++)
		{
			if (-1 == parents[i])
			{
				m_root = i;
			}
			else
			{
				m_vNeiBo[parents[i]].emplace_back(i);
			}
		}
	}
	vector<vector<int>> m_vNeiBo;
	int m_root = -1;
};
class CBFSLeve {
public:
	static vector<int> Leve(const vector<vector<int>>& neiBo, vector<int> start) {
		vector<int> leves(neiBo.size(), -1);
		for (const auto& s : start) {
			leves[s] = 0;
		}
		for (int i = 0; i < start.size(); i++) {
			for (const auto& next : neiBo[start[i]]) {
				if (-1 != leves[next]) { continue; }
				leves[next] = leves[start[i]] + 1;
				start.emplace_back(next);
			}
		}
		return leves;
	}
	template<class NextFun>
	static vector<int> Leve(int N, NextFun nextFun, vector<int> start) {
		vector<int> leves(N, -1);
		for (const auto& s : start) {
			leves[s] = 0;
		}
		for (int i = 0; i < start.size(); i++) {
			auto nexts = nextFun(start[i]);
			for (const auto& next : nexts) {
				if (-1 != leves[next]) { continue; }
				leves[next] = leves[start[i]] + 1;
				start.emplace_back(next);
			}
		}
		return leves;
	}
	static vector<vector<int>> LeveNodes(const vector<int>& leves) {
		const int iMaxLeve = *max_element(leves.begin(), leves.end());
		vector<vector<int>> ret(iMaxLeve + 1);
		for (int i = 0; i < leves.size(); i++) {
			ret[leves[i]].emplace_back(i);
		}
		return ret;
	};
	static vector<int> LeveSort(const vector<int>& leves) {
		const int iMaxLeve = *max_element(leves.begin(), leves.end());
		vector<vector<int>> leveNodes(iMaxLeve + 1);
		for (int i = 0; i < leves.size(); i++) {
			leveNodes[leves[i]].emplace_back(i);
		}
		vector<int> ret;
		for (const auto& v : leveNodes) {
			ret.insert(ret.end(), v.begin(), v.end());
		}
		return ret;
	};
};

class CParents
{
public:
	CParents(vector<int>& vParent, long long iMaxDepth)
	{
		int iBitNum = 0;
		for (; iMaxDepth; iBitNum++) {
			const auto mask = 1LL << iBitNum;
			if (mask & iMaxDepth) { iMaxDepth = iMaxDepth ^ mask; }
		}
		const int n = vParent.size();
		m_vParents.assign(iBitNum + 1, vector<int>(n, -1));
		m_vParents[0] = vParent;
		//树上倍增
		for (int i = 1; i < m_vParents.size(); i++)
		{
			for (int j = 0; j < n; j++)
			{
				const int iPre = m_vParents[i - 1][j];
				if (-1 != iPre)
				{
					m_vParents[i][j] = m_vParents[i - 1][iPre];
				}
			}
		}
	}
	int GetParent(int iNode, int iDepth)const
	{
		int iParent = iNode;
		for (int iBit = 0; iBit < m_vParents.size(); iBit++)
		{
			if (-1 == iParent)
			{
				return iParent;
			}
			if (iDepth & (1 << iBit))
			{
				iParent = m_vParents[iBit][iParent];
			}
		}
		return iParent;
	}
	inline int GetBitCnt()const { return m_vParents.size(); };
	inline const int& GetPow2Parent(int iNode, int n)const {
		return m_vParents[n][iNode];
	}
	//在leftNodeExclude的1到rightLeve级祖先中查找符合pr的最近祖先
	template<class _Pr>
	int FindFirst(int leftNodeExclude, int rightLeve, _Pr pr) {
		for (int iBit = GetBitCnt() - 1; iBit >= 0; iBit--) {
			const int iMask = 1 << iBit;
			if (!(iMask & rightLeve)) { continue; }
			if (pr(m_vParents[iBit][leftNodeExclude])) {
				return BFindFirst(leftNodeExclude, iBit, pr);
			}
			leftNodeExclude = m_vParents[iBit][leftNodeExclude];
		}
		return -1;
	}
protected:
	//在leftNodeExclude的1到2^pow^级祖先中查找符合pr的最近祖先
	template<class _Pr>
	int BFindFirst(int leftNodeExclude, int pow, _Pr pr) {
		while (pow > 0) {
			const int& mid = m_vParents[pow - 1][leftNodeExclude];
			if (pr(mid)) {
			}
			else {
				leftNodeExclude = mid;
			}
			pow--;
		}
		return m_vParents[0][leftNodeExclude];
	}
	vector<vector<int>> m_vParents;
};

class CNeiBo
{
public:
	static vector<vector<int>> Two(int n, const vector<pair<int, int>>& edges, bool bDirect, int iBase = 0)
	{
		vector<vector<int>>  vNeiBo(n);
		for (const auto& [i1, i2] : edges)
		{
			vNeiBo[i1 - iBase].emplace_back(i2 - iBase);
			if (!bDirect)
			{
				vNeiBo[i2 - iBase].emplace_back(i1 - iBase);
			}
		}
		return vNeiBo;
	}
	static vector<vector<int>> Two(int n, const vector<vector<int>>& edges, bool bDirect, int iBase = 0)
	{
		vector<vector<int>>  vNeiBo(n);
		for (const auto& v : edges)
		{
			vNeiBo[v[0] - iBase].emplace_back(v[1] - iBase);
			if (!bDirect)
			{
				vNeiBo[v[1] - iBase].emplace_back(v[0] - iBase);
			}
		}
		return vNeiBo;
	}
	static vector<vector<std::pair<int, int>>> Three(int n, vector<vector<int>>& edges, bool bDirect, int iBase = 0)
	{
		vector<vector<std::pair<int, int>>> vNeiBo(n);
		for (const auto& v : edges)
		{
			vNeiBo[v[0] - iBase].emplace_back(v[1] - iBase, v[2]);
			if (!bDirect)
			{
				vNeiBo[v[1] - iBase].emplace_back(v[0] - iBase, v[2]);
			}
		}
		return vNeiBo;
	}
	static vector<vector<std::pair<int, int>>> Three(int n, const vector<tuple<int, int, int>>& edges, bool bDirect, int iBase = 0)
	{
		vector<vector<std::pair<int, int>>> vNeiBo(n);
		for (const auto& [u, v, w] : edges)
		{
			vNeiBo[u - iBase].emplace_back(v - iBase, w);
			if (!bDirect)
			{
				vNeiBo[v - iBase].emplace_back(u - iBase, w);
			}
		}
		return vNeiBo;
	}
	static vector<vector<int>> Mat(vector<vector<int>>& neiBoMat)
	{
		vector<vector<int>> neiBo(neiBoMat.size());
		for (int i = 0; i < neiBoMat.size(); i++)
		{
			for (int j = i + 1; j < neiBoMat.size(); j++)
			{
				if (neiBoMat[i][j])
				{
					neiBo[i].emplace_back(j);
					neiBo[j].emplace_back(i);
				}
			}
		}
		return neiBo;
	}
};

class Solution {
public:
	vector<float> Ans(int N, const vector<tuple<int, int, int>>& edge) {
		m_iN = N;
		m_neiBo = CNeiBo::Three(N, edge, false, 1);
		m_vk.assign(N, -100); m_vb.assign(N, 0);
		for (int i = 0; i < N; i++)
		{
			if (-100 != m_vk[i]) { continue; }
			BFS(i);
		}
		if (!Check(m_vb, edge)) { return {}; }
		return m_vb;
	}
	void BFS(int root) {
		vector<int> que; vector<bool> vis(m_iN);
		vector<float> bs;
		que.emplace_back(root); vis[root] = true; m_vk[root] = 1;
		float x = 2e10;
		for (int i = 0; i < que.size(); i++) {
			const auto cur = que[i];
			for (const auto& [next, w] : m_neiBo[cur]) {
				const auto k1 = -m_vk[cur];
				const auto b1 = w - m_vb[cur];
				if (-100 == m_vk[next]) {
					m_vk[next] = k1; m_vb[next] = b1;
				}
				else if ((x > 1e10) && (m_vk[next] != k1)) {
					x = (m_vb[next] - b1) / (k1 - m_vk[next]);
				}
				if (!vis[next]) {
					vis[next] = true; que.emplace_back(next);
				}
			}
		}
		if (x > 1e10)
		{
			for (const auto& cur : que) {
				if (1 == m_vk[cur]) {
					bs.emplace_back(-m_vb[cur]);
				}
				else if (-1 == m_vk[cur]) {
					bs.emplace_back(m_vb[cur]);
				}
			}
			if (bs.size()) {
				nth_element(bs.begin(), bs.begin() + bs.size() / 2, bs.end());
				x = bs[bs.size() / 2];
			}
		}
		for (const auto& cur : que) {
			m_vb[cur] += m_vk[cur] * x;
		}
	}
	static bool Check(vector<float>& res, vector<tuple<int, int, int>> edge) {
		for (auto [u, v, w] : edge) {
			u--, v--;
			if (abs(res[u] + res[v] - w) > 0.00001) { return false; }
		}
		return true;
	}
	int m_iN;
	vector<int> m_vk;
	vector<float> m_vb;
	vector<vector<std::pair<int, int>>> m_neiBo;
};

int main() {
#ifdef _DEBUG
	freopen("a.in", "r", stdin);
#endif // DEBUG	
	ios::sync_with_stdio(0); cin.tie(nullptr);
	int n;
	cin >> n;
	auto edge = Read<tuple<int, int, int>>();
	
#ifdef _DEBUG		
	printf("N=%d",n);
	//cout << ",s=" << s;
	Out(edge, ",edge=");
	//Out(ws, ",hs=");
	//Out(que, ",que=");
	/*Out(que, "que=");*/
#endif // DEBUG		
	auto res = Solution().Ans(n,edge);
	cout << (res.size()?"YES":"NO" )<< '\n';
	for (const auto& f : res) {
		cout << f << " ";
	}
	return 0;
}

单元测试

cpp 复制代码
	void Check(vector<float>& res ,float sum, const vector<tuple<int, int, int>>& edge) {
			float actsum = 0;
			for (const auto& f : res) {
				actsum += abs(f);
			}
			Assert::IsTrue(abs(actsum - sum) < 0.00001);
			Assert::IsTrue(Solution::Check(res, edge));
		}
		int N;
		vector<tuple<int, int, int>> edge;
		TEST_METHOD(TestMethod1)
		{
			N = 4, edge = { {1,2,1},{2,3,2},{1,3,2},{3,4,1} };
			auto res = Solution().Ans(N, edge);
			Check(res, 3, edge);
		}
		TEST_METHOD(TestMethod2)
		{
			N = 2, edge = { {1,2,1} };
			auto res = Solution().Ans(N, edge);
			Check(res, 1, edge);
		}
		TEST_METHOD(TestMethod3)
		{
			N = 3, edge = { {1,2,2},{2,3,2} };
			auto res = Solution().Ans(N, edge);
			Check(res, 2, edge);
		}
		TEST_METHOD(TestMethod4)
		{
			N = 3, edge = { {1,2,2},{2,2,1},{2,1,1},{1,2,2} };
			auto res = Solution().Ans(N, edge);
			AssertEx(0u, res.size());
		}

扩展阅读

我想对大家说的话
工作中遇到的问题,可以按类别查阅鄙人的算法文章,请点击《算法与数据汇总》。
学习算法:按章节学习《喜缺全书算法册》,大量的题目和测试用例,打包下载。重视操作
有效学习:明确的目标 及时的反馈 拉伸区(难度合适) 专注
闻缺陷则喜(喜缺)是一个美好的愿望,早发现问题,早修改问题,给老板节约钱。
子墨子言之:事无终始,无务多业。也就是我们常说的专业的人做专业的事。
如果程序是一条龙,那算法就是他的是睛
失败+反思=成功 成功+反思=成功

视频课程

先学简单的课程,请移步CSDN学院,听白银讲师(也就是鄙人)的讲解。
https://edu.csdn.net/course/detail/38771

如何你想快速形成战斗了,为老板分忧,请学习C#入职培训、C++入职培训等课程
https://edu.csdn.net/lecturer/6176

测试环境

操作系统:win7 开发环境: VS2019 C++17

或者 操作系统:win10 开发环境: VS2022 C++17

如无特殊说明,本算法用**C++**实现。

相关推荐
嵌入式-老费3 小时前
Easyx使用(中篇)
算法
信奥卷王3 小时前
[GESP202312 五级] 烹饪问题
算法
云雾J视界3 小时前
算法偏见的解药:将敏捷“灵魂”注入AI伦理
人工智能·算法·谷歌·伦理债·算法偏见·高效程序员·ai决策系统
青草地溪水旁4 小时前
设计模式(C++)详解——备忘录模式(2)
c++·设计模式·备忘录模式
小张成长计划..4 小时前
STL简介
c++
CHANG_THE_WORLD4 小时前
函数简单传入参数的汇编分析
汇编·c++·算法
安全风信子4 小时前
45_混合专家模型:MoE架构详解
算法
ASIAZXO4 小时前
机器学习——聚类kmeans算法详解
算法·机器学习·聚类