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

本题解同步于luogu

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

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

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

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

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

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

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

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

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

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

\[\text{那么直接dfs}\]

时间复杂度为\[O(T\times(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标准写成,代码中不同不同语法处已标注

猜你喜欢

转载自www.cnblogs.com/tyqtyq/p/10387591.html