• NOIP2010提高组复赛C 关押罪犯


    题目链接:https://ac.nowcoder.com/acm/contest/258/C

    题目大意:

      略

    分析:

      这题是并查集的一个变题,先按积怨值从大到小排序,然后一个一个看能否完全分开,遇到的第一个不能分开的囚犯对(如果强行分开就必然有更高的积怨值出现)就是答案。
      一开始想到的是按监狱数量弄个并查集,后来发现并不行,因为如果要分开一对囚犯,没办法决定谁一定住1号监狱,谁一定住2号监狱。后来试了下用囚犯数量弄并查集,发现也不行,因为没有积怨的才能放一个集合里,比如1和2有积怨不在一起,3和4有积怨不在一起,那1和3,1和4等等就没法确定,那把他们都放不同集合里?不行,因为不在一个集合的可能有积怨可能没积怨。
      让后我就想当选取到i + 1对囚犯时,前i对囚犯必然也形成一张图,这张图可能不是连通的,换句话说,就是包含多个极大联通子图(囚犯小团体),小团体与小团体之间互相没有积怨,因为程序已经选取到了i + 1对囚犯,所以这些小团体内部必然可以两两分开以致于没有积怨。选取其中一个小团体,如果这个极大联通子图没有坏,那必然可以变形成如下形式:
    也就是说,肯定可以一刀切。
    如果有环,那促成环的这条线的两端必分别属于左右两边:
    如果这个时候来了一条边,它的两个端点都在这张图的一边:
    那这张图必然怎么切都切不开了。
    也就是说,如果第i + 1对囚犯都属于某一个小团体的一边,答案就出来了。
    也就是说每名囚犯应该有2个状态i和i',上面的图应该是这样:
    于是并查集的长度应该为2n,前n个表示1~n,后n个表示1'~n'。
    PS:代码中并没有判断是否在都一边而是直接判断在不在一个集合,这是因为是直接查找的同一边的点,就是没有'的那边,这样直接判断在不在一个集合就可以了。

    代码如下:

     1 #include <bits/stdc++.h>
     2 using namespace std;
     3  
     4 #define rep(i,n) for (int i = 0; i < (n); ++i)
     5 #define For(i,s,t) for (int i = (s); i <= (t); ++i)
     6 #define rFor(i,t,s) for (int i = (t); i >= (s); --i)
     7 #define foreach(i,c) for (__typeof(c.begin()) i = c.begin(); i != c.end(); ++i)
     8 #define rforeach(i,c) for (__typeof(c.rbegin()) i = c.rbegin(); i != c.rend(); ++i)
     9  
    10 #define pr(x) cout << #x << " = " << x << "  "
    11 #define prln(x) cout << #x << " = " << x << endl
    12  
    13 #define ALL(x) x.begin(),x.end()
    14 #define INS(x) inserter(x,x.begin())
    15  
    16 #define ms0(a) memset(a,0,sizeof(a))
    17 #define msI(a) memset(a,inf,sizeof(a))
    18  
    19 #define pii pair<int,int> 
    20 #define piii pair<pair<int,int>,int> 
    21 #define mp make_pair
    22 #define pb push_back
    23 #define fi first
    24 #define se second
    25  
    26 inline int gc(){
    27     static const int BUF = 1e7;
    28     static char buf[BUF], *bg = buf + BUF, *ed = bg;
    29      
    30     if(bg == ed) fread(bg = buf, 1, BUF, stdin);
    31     return *bg++;
    32 } 
    33  
    34 inline int ri(){
    35     int x = 0, f = 1, c = gc();
    36     for(; c<48||c>57; f = c=='-'?-1:f, c=gc());
    37     for(; c>47&&c<58; x = x*10 + c - 48, c=gc());
    38     return x*f;
    39 }
    40  
    41 typedef long long LL;
    42 const int maxN = 1e5 + 7;
    43  
    44 struct Edge{
    45     int X, Y, W;
    46      
    47     Edge() {}
    48     Edge(int x, int y, int w) : X(x), Y(y), W(w) {}
    49      
    50     bool operator < (const Edge &x) const {
    51         return W > x.W;
    52     }
    53 };
    54  
    55 int n, m, ans;
    56 Edge e[maxN];
    57 int f[maxN];
    58  
    59 int Find(int x){
    60     while (x != f[x]) x = f[x] = f[f[x]];
    61     return x;
    62 } 
    63  
    64 int main(){
    65     n = ri();
    66     m = ri();
    67      
    68     For(i, 1, m) {
    69         e[i].X = ri();
    70         e[i].Y = ri();
    71         e[i].W = ri();
    72     }
    73     sort(e+1, e+m+1);
    74     For(i, 1, n<<1) f[i] = i;
    75      
    76     For(i, 1, m) {
    77         int x = e[i].X, y = e[i].Y;
    78         x = Find(x);
    79         y = Find(y);
    80         if(x == y) {
    81             ans = e[i].W;
    82             break;
    83         }
    84         f[x] = Find(e[i].Y + n);
    85         f[y] = Find(e[i].X + n);
    86     }
    87      
    88     printf("%d
    ", ans);
    89     return 0;
    90 }
    View Code
  • 相关阅读:
    开发脚本自动部署及监控
    内存进程与软件包安装
    Linux常用指令
    网络协议
    Python基础(二)
    python基础(一)
    shell编程
    正则与sed,grep,awk三剑客
    网络配置和元字符
    nginx
  • 原文地址:https://www.cnblogs.com/zaq19970105/p/10752977.html
Copyright © 2020-2023  润新知