2017 Multi-University Training Contest

来源:互联网 发布:strcontains php 编辑:程序博客网 时间:2024/06/07 14:34

传送门
//题意: 给一幅仙人掌图, 问前k小的生成树的权值, 结合题目所给式子.
//思路:由于图是一个仙人掌,所以显然对于图上的每一个环都需要从环上取出一条边删掉. 所以问题就变为有 M 个集合,每个集合里面都有一堆数字, 要从每个集合中选择一个恰好一个数加起来. 求所有的这样的和中,前 K 大的是哪些. 这就是一个经典问题了. ( 然而我并不知道 )
多校第一场题解
//收获: 点双联通就不说了, 难点在于求前K大, 这里是多个集合, 看了题解才知道原来是这样, 先前两个合并, 然后和后面的每一个集合合并. 想法很棒, 这次长知识了, 处理的也很好.

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});    }       //首先把当前第一个答案数组中的数(V中的最大)一一加上新加的数丢进队列.        //x: i 代表新加的数组的下标, y: 0 是当前答案数组加的下标.    vector<int > v;    W.resize(0);    //目标就是维护一个前K大的序列.    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;}

代码就贴标程把, 我自己写的vector 一直被卡, 这个标程的vector也是3950ms左右. 其实我自己的改的跟标程差不多了, 还是T. 所以还是贴标程.
重要的是这道题的处理思想 !
AC Code

#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});    }       //首先把当前第一个答案数组中的数(V中的最大)一一加上新加的数丢进队列.        //x: i 代表新加的数组的下标, y: 0 是当前答案数组加的下标.    vector<int > v;    W.resize(0);    //目标就是维护一个前K大的序列.    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里面没有数.    res.push_back(0);  //所以需要先推进一个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();    }}
原创粉丝点击