• @hdu



    @description@

    n 个队伍两两之间比赛,保证没有平局。

    现在给出 n 个队伍分别的获胜次数,询问这些数据是否合法。

    Input
    先给出 T(T≤50) 表示数据组数。
    对于每组数据给出 n(n≤50000) 表示参赛队伍数。
    接下来 n 行,第 i 给出一个整数 ai (0≤ai<n) 表示第 i 个队伍的获胜次数。

    Output
    对于每组数据,若不合法输出 "The data have been tampered with!",否则输出 "It seems to have no problem."。

    Sample Input
    2
    3
    2
    1
    0
    3
    2
    2
    2
    Sample Output
    It seems to have no problem.
    The data have been tampered with!

    Hint:
    对于第一组数据,一种可能的情况是队伍 1 战胜队伍 2, 3,队伍 2 战胜队伍 3。
    对于第二组数据,不难发现不存在一种可能的胜负情况。

    @solution@

    竞赛图的判定,有一个叫作兰道定理的东西。
    但是我这里不详细讲兰道定理的一般证明方法,只提供我在网上看到的一个有趣的推导。

    假如胜者向败者连边,则题目相当于给出竞赛图中所有点的出度,问是否存在合法的图。
    考虑给无向完全图的边定向得到竞赛图,我们定向为 u->v 时,u 所需要的出度就会少 1。
    这会让我们联想起混合图欧拉路中,也是通过给无向边定向调整入度出度。于是联想到网络流。

    考虑如下的建模:
    从 s 向 n*(n-1) 条边连容量为 1 的边;假如第 i 条边为 (u, v),则 i 向 u 和 v 各连一条容量为 1 的边;最后 n 个顶点向 t 连容量为 a[i] 的边。
    假如这个网络流能够满流,则对应原题目中的合法。

    但是显然我们直接跑是跑不了的,考虑怎么样才能直接判定。
    如果我们将 n 个顶点每一个点拆成 a[i] 个点,则问题可以转为二分图完美匹配的判定性问题,于是就可以引入 hall 定理。

    考虑从左边选出一个集合对应的右边的邻集意义是什么,不难发现其实是从原图中选出一个边集,每条边连接的原图中的点的集合。
    hall 定理要求邻集必须比当前集合大。反过来,我们考虑给定一个邻集(即对应原图中的点集),能够得到的最大的当前集合有多大——其实就是边集在原图中形成完全图时最大。
    于是,我们可以在本题中将 hall 定理等价地推为 (frac{|S|*(|S|-1)}{2} le sum_{iin S}a[i]),对于原图中任意一个点集 S 都要满足。
    注意到左边只与 S 的大小有关,我们只需要考虑 S 同等大小的情况下 (sum_{iin S}a[i]) 的最小值。贪心地想这个最小值一定是 a 中前 |S| 小的加起来。

    于是:只需要将 a 排好序,对于每一个 i,如果满足 a[1] + a[2] + ... + a[i] >= i*(i-1)/2 即有解。
    当然因为要求完美匹配,所以左右大小应该相等,即应该有 a[1] + a[2] + ... + a[n] = n*(n-1)/2。

    其实上面的判别式就是兰道定理。

    @accepted code@

    #include<cstdio>
    #include<algorithm>
    using namespace std;
    typedef long long ll;
    const int MAXN = 50000;
    int a[MAXN + 5], n;
    void solve() {
    	scanf("%d", &n);
    	for(int i=1;i<=n;i++)
    		scanf("%d", &a[i]);
    	sort(a + 1, a + n + 1);
    	ll sum = 0;
    	for(int i=1;i<=n;i++) {
    		sum += a[i];
    		if( sum < 1LL*i*(i-1)/2 ) {
    			puts("The data have been tampered with!");
    			return ;
    		}
    	}
    	if( sum == 1LL*n*(n-1)/2 )
    		puts("It seems to have no problem.");
    	else puts("The data have been tampered with!");
    }
    int main() {
    	int T; scanf("%d", &T);
    	while( T-- ) solve();
    }
    

    @details@

    至于兰道定理真正的证明。。。没有必要记吧。。。记忆一个 hall 定理已经够累了。。。

  • 相关阅读:
    用java在mysql中随机插入9000 000条数据
    java连接mysql的一个小例子
    JDK环境变量配置
    JVM工作原理
    线程和进程的区别
    java实现链表
    内连接、外连接、左连接、右连接
    udp协议
    要看的东西
    eclipse快捷键
  • 原文地址:https://www.cnblogs.com/Tiw-Air-OAO/p/11381447.html
Copyright © 2020-2023  润新知