1. 程式人生 > >題解【[FJOI2018]所羅門王的寶藏】

題解【[FJOI2018]所羅門王的寶藏】

con define 思路 line 改進 turn .so 增加 stdin

本題解同步於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標準寫成,代碼中不同不同語法處已標註

題解【[FJOI2018]所羅門王的寶藏】