题解【[FJOI2018]所罗门王的宝藏】

本题解同步于luogu

emmm切了近年省选题来写题解啦qwq

该题较其他省选题较水吧(否则我再怎么做的出来

思路是图论做法,做法上楼上大佬已经讲的很清楚了,我来谈谈代码实现上的一些细节

[ ext{设节点1...2n,i}in ext{1-n表示i行,i}in ext{(n+1)-2n时表示i-n列} ]

[ ext{当我们读到一颗绿宝石(x,y,k)时,就从x向y+n连一条权值为k的边} ]

[ ext{当我们连完边后会发现给一行/一列增加a就相当于把与这个点相连的所有边权值增加a} ]

[ ext{这个加边权可以转化为加点权} ]

[ ext{设}onk_i ext{表示在这个节点上的点击次数,} ]

[ ext{搜索起始节点的初值为与这个节点所连边中权值最小的} ]

[ ext{那么已知两点i,j以及}edge_{i,j} ext{和}onk_i ext{,那么由题目条件易得}onk_j=edge_{i,j}-onk_i ]

[ ext{那么直接dfs} ]

时间复杂度为$$O(T imes(KlogK+K)) = O(TNlogN)$$要改进也行,因为我们对于每个点所连边只要边权最小数所以没必要sort,但当我想到这一点时已经AC本题~

[Talk;is;free;,;show;me;the;code ]

#include<iostream>
#include<cstdio>
#include<vector>
#include<algorithm>
#include<cstring>
#define MAXN 1005
using namespace std ;
inline void read(int &x) {
	scanf("%d",&x) ;
}
class getsol {
	public:
		//========data========
		vector<pair<int,int> > edge[MAXN*2] ; //pair第一维是边权,第二维是到达边的编号
		int n , m , k , onk[MAXN*2] , inq[MAXN*2] , flag ;//inq表示是否被搜到
		//========func========
		void add(int x,int y,int v) {
			edge[x].push_back(make_pair(v,y)) ; //加边
		}
		bool check(int u,int v,int w) {
			//check , 判断v点是否可行
			if(onk[u]+onk[v]!=w) return 0 ;
			return 1 ;
		}
		void dfs(int D) {
			//cout<<"DFS : START SEARCH IN DOT "<<D<<endl ;
			if(flag==0) return ;
			inq[D] = 1 ;
			for(auto& i : edge[D]) { //对于每个edge[D]中的元素i
				///cout<<"DFS : SEARCH IN DOT "<<i.second<<endl ;
				if(flag==0) return ;
				//cout<<"In dot : "<<i.second<<endl ;
				int ver = i.second , edgeval = i.first ;
				if(inq[ver]) {
					if(flag==1) //如果答案还是"Yes"那么更新,这里是一个优化~
						flag = check(D,ver,edgeval) ;
					continue ;
				} else {
					onk[ver] = edgeval-onk[D] ;
					dfs(ver) ;
				}
			}
		}
		void PRINT(int* arr,int n) {
			for(int i=1; i<=n; ++i) {
				cout<<"arr["<<i<<"] = "<<arr[i]<<endl ;
			}
		}
		void sol() {
			flag = true ;
			read(n) , read(m) , read(k) ;
			//行的编号为1~n
			//列的编号为(n+1)~(2*n)
			//喵~
			for(int i=1; i<=k; ++i) {
				int x,y,v ;
				read(x) , read(y) , read(v) ;
				add(x,y+n,v) ;
				add(y+n,x,v) ;
			}
			//cerr<<"FINISH READ"<<endl ;
			for(int i=1; i<=2*n; ++i) sort(edge[i].begin(),edge[i].end()) ;
			//cerr<<"FINISH SORT"<<endl ;
			for(int i=1; i<=2*n; ++i) {
				if(!inq[i]&&flag&&!edge[i].empty()) { // 注意这里判一下vector是否为空。。因为这个RE了两三次
					onk[i] = (*edge[i].begin()).first ;
					//cerr<<"SEARCH IN DOT "<<i<<endl ;
					dfs(i) ;
				}
			}
			if(flag==1) {
				for(int i=1; i<=2*n; ++i)
					for(auto& j : edge[i])
						if(flag==1) //重新check一遍,以免遗漏
							flag = check(i,j.second,j.first) ;
			}
			if(flag) puts("Yes") ;
			else puts("No") ;
			//PRINT(onk,2*n) ;
		}
		void clear() {
			for(int i=1; i<=2*n; ++i) edge[i].clear() ;
			memset(inq,0,sizeof(onk)) ;
			memset(onk,0,sizeof(onk)) ;
			n = m = k = flag = 0 ;
		}
} ;
getsol M ;
int T ;
int main() {
	//freopen("solo3.in" , "rb" , stdin) ;
	//freopen("solo3.out", "wb" ,stdout) ;
	read(T) ;
	while(T--) M.sol() , M.clear() ;
}

注意本代码是使用C++11标准写成,代码中不同不同语法处已标注

原文地址:https://www.cnblogs.com/tyqtyq/p/10387591.html