6041 I Curse Myself(點雙聯通加集合合并求前K大) 2017多校第一場

來源:互聯網
上載者:User

標籤:需要   簡單   color   self   its   產生樹   這一   官方   過程   

題意:

 給出一個仙人掌圖,然後求他的前K小產生樹。

思路:

先給出官方題解

由於圖是一個仙人掌,所以顯然對於圖上的每一個環都需要從環上取出一條邊刪掉。所以問題就變
為有 M 個集合,每個集合裡面都有一堆數字,要從每個集合中選擇一個恰好一個數加起
來。求所有的這樣的和中,前 K 大的是哪些。這就是一個經典問題了。

 

點雙聯通就不說了 都一眼能看出來做法就是縮點之後每個環每次取一個,然後找最大的k個所以這道題的痛點就在這裡,做法當然是不知道啦,看了題解和部落格才懂的。以前做過兩個集合合并的,這個是k個合并,和兩個集合合并差不多,不過感覺很厲害啊。。就是先兩個合并,然後和第三個合并,在和第四個,然後就可以了。。複雜度的話題解說的O(KM)。。。那就O(KM)吧,反正這個是真不會證過程挺厲害的 在合并的時候

 

void merge(vector<int> &V , vector<int> B) {   priority_queue< opt > Q;   for (int i = 0 ; i < B.size() ; ++ i) {       Q.push((opt) {V[0] + B[i] , i , 0});   }   W.resize(0);   while (W.size() < K && !Q.empty()) {       auto it = Q.top(); Q.pop();       W.push_back(it.w);       if (it.y + 1 < V.size()) {           ++ it.y;           Q.push((opt) {B[it.x] + V[it.y] , it.x , it.y});       }   }   V = W;}

最開始一直沒看懂  看了一天呀。。。  後來想通了感覺還是挺簡單的,然後在本機跑,沒開O2跑了大半輩子。。。

具體過程其實就是對於每個已經合并了的數組,最開始讓他最大的和B數組合并,然後對於已經合并了的數組,如果當前

的這一位被push進答案數組,那麼就把先合并數組的次大的和B數組的當前數字合并就可以了。

思路挺厲害的~

 

 代碼就貼標程的吧~

#include <bits/stdc++.h>using namespace std;const int N = 1005;int n , m , K;struct opt {    int w , x , y;    bool operator < (const opt& R) const {        return w < R.w;    }};vector<int> W;void merge(vector<int> &V , vector<int> B) {    priority_queue< opt > Q;    for (int i = 0 ; i < B.size() ; ++ i) {        Q.push((opt) {V[0] + B[i] , i , 0});    }    W.resize(0);    while (W.size() < K && !Q.empty()) {        auto it = Q.top(); Q.pop();        W.push_back(it.w);        if (it.y + 1 < V.size()) {            ++ it.y;            Q.push((opt) {B[it.x] + V[it.y] , it.x , it.y});        }    }    V = W;}int pre[N] , mcnt;struct edge {    int x , w , next;} e[N << 2];vector<int> res;int dfn[N] , low[N] , ncnt;stack<int> S;void dfs(int x , int fa) {    dfn[x] = low[x] = ++ ncnt;    for (int i = pre[x] ; ~i ; i = e[i].next) {        int y = e[i].x;        if (!dfn[y]) {            S.push(i);            dfs(y , i ^ 1);            low[x] = min(low[x] , low[y]);            if (low[y] > dfn[x]) {}//(x , y) is bridge            if (low[y] >= dfn[x]) {                int j;                vector<int> V;                do {                    j = S.top();                    S.pop();                    V.push_back(e[j].w);                } while (j != i);                if (V.size() > 1) {                    //cout << V.size() << endl;                    //for (auto &x : V) cout << x << ‘ ‘; cout << endl;                    merge(res , V);                }            }        } else if (i != fa && dfn[y] < dfn[x])            S.push(i) , low[x] = min(low[x] , dfn[y]);    }}void work() {    memset(pre , -1 , sizeof(pre));    mcnt = ncnt = 0;    int sum = 0;    for (int i = 0 ; i < m ; ++ i) {        int x , y , z;        scanf("%d%d%d" , &x , &y , &z);        e[mcnt] = (edge) {y , z , pre[x]} , pre[x] = mcnt ++;        e[mcnt] = (edge) {x , z , pre[y]} , pre[y] = mcnt ++;        sum += z;    }    scanf("%d" , &K);    res.resize(0);    res.push_back(0);    memset(dfn , 0 , sizeof(dfn));    dfs(1 , 0);    int w = 0;    for (int i = 0 ; i < res.size() ; ++ i) {        w += (i + 1) * (sum - res[i]);    }    static int ca = 0;    printf("Case #%d: %u\n" , ++ ca , w);}int main() {    res.reserve(100001);    W.reserve(100001);    while (~scanf("%d%d" , &n , &m)) {        work();    }    return 0;}

 

6041 I Curse Myself(點雙聯通加集合合并求前K大) 2017多校第一場

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.