当前位置: 首页 > news >正文

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

本文涉及知识点

数学
C++DFS
C++贪心

P6696 [BalticOI 2020] 图 (Day2)

题目描述

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

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

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

求一种点权的分配方案。

输入格式

第一行两个整数 N,MN,MN,M 代表点数和边数。
所有点的编号为 111NNN
接下来 MMM 行每行三个整数 a,b,ca,b,ca,b,c 描述一条边端点为 aaabbb,如果 ccc111 那么这条边是黑色边,如果 ccc222 那么这条边是红色边。

输出格式

如果有解,首先第一行输出 YES,然后第二行 NNN 个整数代表可能的一组点权。
多组解输出任意一组即可。
如果无解,一行一个字符串 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

说明/提示

评测方式

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

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

本题采用捆绑测试。

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

对于 100%100\%100% 的数据,1≤N≤1051 \le N \le 10^51N1050≤M≤2×1050 \le M \le 2 \times 10^50M2×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后,计算本连通区域各点的值。

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

#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 _DEBUGfreopen("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。
如果:k1==k2,b1==b2k1 == k2,b1 == b2k1==k2b1==b2, 忽略。
如果:k1==k2,b1≠b2k1 == k2, b1 \neq b2k1==k2,b1=b2 ,出错。
如果:k1≠k2k1 \neq k2k1=k2 ,则 x=b2−b1k1−k2x= \frac{b2-b1}{k1-k2}x=k1k2b2b1
如果没有计算出x,则通同过中位数贪心计算x。
最后将计算的结果演算判断是否无解。中间可以不判断非法,最后统一判断。

核心代码

#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 _DEBUGfreopen("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;
}

单元测试

	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++**实现。

http://www.dtcms.com/a/436208.html

相关文章:

  • 做直播网站软件有哪些软件wordpress代码seo
  • 网站建设皿金手指谷哥壹柒wordpress 七牛视频教程
  • 什么是网站前置审批wordpress paginate_comments_links
  • Vue3基础入门
  • 农村网站建设的意义文明农村建设网站
  • wordpress打通公众号百度优化只做移动网站没有pc站
  • 无人机找工作去哪找?无人机接单app
  • 王健林亏60亿做不成一个网站wordpress always主题
  • node可以做电商网站么企业网站可以自己做
  • 医疗网站seo怎么做小程序在哪里
  • 语言可以做网站吗国家高新技术企业公示名单
  • 租赁服务器的网站云服务器搭建网站教程
  • 医程通 网站做的太中装建设庄重
  • 网站推广都做什么内容网站建设颜色代码
  • 响应式网站空间服务器要求idc自动续费网站源码
  • MySQL授权super权限
  • 做淘客网站要备案企业网站有什么功能
  • go中的切片
  • 个人网站模板html代码免费百度旧版本下载
  • 网站构建的过程ftp修改wordpress密码
  • 胡恩全10.2作业
  • 增城高端网站建设wordpress 国内不使用方法
  • 百度推广帮做网站长春网站推广优化
  • 【密码学实战】openHiTLS dgst命令行:消息摘要与数字签名
  • 淘宝 网站建设教程视频wordpress栏目id顺序
  • 目前热门的网站建设语言企业网站建设网站优化推广
  • 厦门建网站熊掌号结合网站做seo
  • wordpress站点地图优化网站开发内容怎么写
  • 免费商城版网站广州seo网络推广员
  • php做网站如何架构怎么提高网站访问速度