zoukankan      html  css  js  c++  java
  • 题解【[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标准写成,代码中不同不同语法处已标注

  • 相关阅读:
    git 教程
    gruntjs
    arm linux
    2021最佳迎接元旦的方式是什么!程序员:中国新冠疫苗上市!
    元旦表白神器!C语言实现浪漫烟花表白(有背景音乐+示例源码)
    大学期间,为啥我能学好C语言?只因我做到了这五点!
    为什么都说代码改变世界?是因为这五位程序员创造了未来!
    C++丨常见的四种求最大公约数方法!赶紧收藏!
    【腾讯C++面试题】如何才能获得腾讯的offer?掌握这20道终身受益!
    惊呆了!字节跳动成唯一上榜的中国公司!它是如何做到脱颖而出的?
  • 原文地址:https://www.cnblogs.com/tyqtyq/p/10387591.html
Copyright © 2011-2022 走看看