竞赛图的得分序列 (SRM 717 div 1 250)

SRM 717 DIV 1 中 出了这样一道题:

竞赛图就是把一个无向完全图的边定向后得到的有向图,得分序列就是每个点的出度构成的序列。

给出一个合法的竞赛图出度序列, 要求构造出原图(原题是求(u, v)有路径的点对数,似乎有不需要构造出原图的方法)。

当时我的做法是 直接构造一个网络,跑最大流。

比赛后总觉得这个题有什么神奇的性质,于是搜了一下相关资料:

有一篇关于得分序列的论文:
http://www.sciencedirect.com/science/article/pii/0095895679900455?via%3Dihub

其中介绍了一个性质:

Landau Theorem: 竞赛图的出度序列为${s_i}$的充要条件是 对于任意的子集X,$\sum\limits_{i \in X} s_i \ge \tbinom{|X|}{2}$

1.必要性很容易证明:如果${s_i}$是竞赛图的得分序列, 对于任意一个子集X, 它的得分之和一定大于等于它内部点之间的得分和。

2.充分性证明: 大致思路是构造一个二分图然后利用Hall定理 证明完美匹配。

 首先把边<i, j> (i < j) 看做左边的点。 右边部分,对于每个$s_i$ 搞出$s_i$个点(这些点记为$i$类点)。

 对于左边的点<i, j> ,  向右边所有的$i$类点和$j$类点各连一条边。 显然一个完美匹配 和 一个原图对应。

根据Hall定理,有完美匹配的充要条件是  对于左边任意的点集X,   $|H(X)| \ge |X|$.  $H(X)$是右边与$X$中的点有边相连的点的集合。

对于左边任意的点集$X$, 设集合$Y$为$X$中的边的端点的集合。 即$Y = {x | (x, t) \in X  \ or\  (t, x) \in X}$

根据所给的条件, 我们有 $ |X| \leq \tbinom{|Y|}{2} \leq \sum\limits_{i \in Y} s_i = |H(X)|$, 所以存在完美匹配。定理得证。

接下来我们怎么用这个定理来构造原图呢?

当然可以构造出二分图然后跑最大匹配。

我自己又YY了一种贪心做法:

大致思想是不断给边定向,但是要让得分序列满足Landau Theorem。

先将所有点按$s_i$ 从小到大排序, 考虑 $s_i$最小的那个点x, 有$n - 1 - s_i$ 条边指向它, 我们确定哪些点向它连边,让这些点的score -1. 显然贪心一下 让score 最大的$n - 1 - s_i$个点 向它连边最优。    对于其它的点, x向它们连边就好。

AC代码:

 1 // BEGIN CUT HERE
 2
 3 // END CUT HERE
 4 #line 5 "ScoresSequence.cpp"
 5 #include <vector>
 6 #include <list>
 7 #include <map>
 8 #include <set>
 9 #include <deque>
10 #include <stack>
11 #include <bitset>
12 #include <algorithm>
13 #include <functional>
14 #include <numeric>
15 #include <utility>
16 #include <sstream>
17 #include <iostream>
18 #include <iomanip>
19 #include <cstdio>
20 #include <cmath>
21 #include <cstdlib>
22 #include <ctime>
23 #include <cstring>
24 using namespace std;
25 #define N 102
26 const int INF = 1e9 + 1;
27
28 bool a[N][N];
29 vector<pair<int, int> > lis, tmp;
30
31
32 class ScoresSequence
33 {
34 public:
35     int count(vector <int> s)
36     {
37         int n = s.size();
38         memset(a, 0, sizeof(a));
39         for (int i = 0; i < n; ++i) a[i][i] = true;
40
41         lis.clear();
42         for (int i = 0; i < n; ++i) lis.push_back(make_pair(s[i], i));
43
44         for (int i = 0; i < n - 1; ++i)
45         {
46             sort(lis.begin(), lis.end());
47             int c = n - i - lis[0].first - 1;
48             for (int k = 1; k <= c; ++k)
49             {
50                 lis[n - i - k].first--;
51                 a[lis[n - i - k].second][lis[0].second] = true;
52             }
53             for (int k = 1; k < n - i - c; ++k)
54                 a[lis[0].second][lis[k].second] = true;
55             tmp.clear();
56             for (int j = 1; j < lis.size(); ++j) tmp.push_back(lis[j]);
57             lis = tmp;
58         }
59         int ans = 0;
60         for (int k = 0; k < n; ++k)
61             for (int i = 0; i < n; ++i)
62                 for (int j = 0; j < n; ++j)
63                     a[i][j] |= a[i][k] & a[k][j];
64         for (int i = 0; i < n; ++i)
65             for (int j = 0; j < n; ++j)
66                 ans += a[i][j];
67         return ans;
68     }
69
70 // BEGIN CUT HERE
71     public:
72     void run_test(int Case) { if ((Case == -1) || (Case == 0)) test_case_0(); if ((Case == -1) || (Case == 1)) test_case_1(); if ((Case == -1) || (Case == 2)) test_case_2(); if ((Case == -1) || (Case == 3)) test_case_3(); if ((Case == -1) || (Case == 4)) test_case_4(); }
73     private:
74     template <typename T> string print_array(const vector<T> &V) { ostringstream os; os << "{ "; for (typename vector<T>::const_iterator iter = V.begin(); iter != V.end(); ++iter) os << ‘\"‘ << *iter << "\","; os << " }"; return os.str(); }
75     void verify_case(int Case, const int &Expected, const int &Received) { cerr << "Test Case #" << Case << "..."; if (Expected == Received) cerr << "PASSED" << endl; else { cerr << "FAILED" << endl; cerr << "\tExpected: \"" << Expected << ‘\"‘ << endl; cerr << "\tReceived: \"" << Received << ‘\"‘ << endl; } }
76     void test_case_0() { int Arr0[] = {2, 0, 1}; vector <int> Arg0(Arr0, Arr0 + (sizeof(Arr0) / sizeof(Arr0[0]))); int Arg1 = 6; verify_case(0, Arg1, count(Arg0)); }
77     void test_case_1() { int Arr0[] = {1, 0, 2}; vector <int> Arg0(Arr0, Arr0 + (sizeof(Arr0) / sizeof(Arr0[0]))); int Arg1 = 6; verify_case(1, Arg1, count(Arg0)); }
78     void test_case_2() { int Arr0[] = {1, 1, 1}; vector <int> Arg0(Arr0, Arr0 + (sizeof(Arr0) / sizeof(Arr0[0]))); int Arg1 = 9; verify_case(2, Arg1, count(Arg0)); }
79     void test_case_3() { int Arr0[] = {0, 2, 8, 4, 3, 9, 1, 5, 7, 6}; vector <int> Arg0(Arr0, Arr0 + (sizeof(Arr0) / sizeof(Arr0[0]))); int Arg1 = 55; verify_case(3, Arg1, count(Arg0)); }
80     void test_case_4() { int Arr0[] = {22,20,14,13,17,15,12,18,23,15,21,26,33,5,19,9,37,0,25,28,4,12,35,32,25,7,31,6,2,29,10,33,36,27,39,28,40,3,8,38,3}; vector <int> Arg0(Arr0, Arr0 + (sizeof(Arr0) / sizeof(Arr0[0]))); int Arg1 = 1422; verify_case(4, Arg1, count(Arg0)); }
81
82 // END CUT HERE
83
84 };
85
86 // BEGIN CUT HERE
87 int main()
88 {
89 ScoresSequence ___test;
90 ___test.run_test(-1);
91 system("pause");
92 }
93 // END CUT HERE  

实现方法比较暴力,大概是 O(n^2 logn)

时间: 2024-11-04 18:55:57

竞赛图的得分序列 (SRM 717 div 1 250)的相关文章

TopCoder SRM 596 DIV 1 250

body { font-family: Monospaced; font-size: 12pt } pre { font-family: Monospaced; font-size: 12pt } Problem Statement      You have an array with N elements. Initially, each element is 0. You can perform the following operations: Increment operation:

Topcoder SRM 687 (Div 2) 250.Quorum

Problem Statement   In one organization they have n different committees. The organization has a very large number of employees. Each employee is a member of each committee. Each committee has a quorum: the smallest number of members that have to be

Topcoder SRM 661 (Div.1) 250 MissingLCM - 数论

[题意] 给你一个数N(1<=N<=10^6),要求最小的M(M>N),使得lcm(n+1,n+2,...m)=lcm(1,2,3,...,m) [思路] 手速太慢啦,等敲完代码的时候发现比赛已经结束了 一开始我想直接枚举m,并判断lcm(1,..,m)与lcm(n+1,n+2,...,m)是否相等,但发现,当求到lcm(1,...,40)的时候就爆LL了 显然不能这样求 也就是说,要求出具体lcm(1,2,...,m)的值是很困难的 怎么求 可以把它分解质因数,分解成几个质数相乘的形式

Topcoder口胡记 SRM 562 Div 1 ~ SRM 592 Div 1

传送门:https://284914869.github.io/AEoj/index.html Topcoder SRM 562 Div 1 - Problem 1000 InducedSubgraphs 当K*2<=N的时候,显而易见的是编号为i(K<=i<=N-K+1)的点一定会形成一条链. 枚举合法的这样的链,剩下的暴力dp吧. 当K*2>N的时候,显而易见的是编号为i(N-K+1<=i<=K)的点一定会形成一个联通快. 如果把这个联通块去掉,树会形成若干个不相交

TopCoder SRM 634 Div.2[ABC]

TopCoder SRM 634 Div.2[ABC] ACM 题目地址: TopCoder SRM 634 赛后做的,感觉现场肯定做不出来Orz,简直不能多说. Level One-MountainRanges[水题] 题意: 问序列中有几个完全大于旁边的峰. 分析: 傻逼题,不多说. 代码: /* * Author: illuz <iilluzen[at]gmail.com> * File: one.cpp * Create Date: 2014-09-26 21:01:23 * Desc

TC Member SRM 478 DIV 1(CarrotJumping-操作观察)

Problem Statement   Rabbits often feel hungry, so when they go out to eat carrots, they jump as quickly as possible. Initially, rabbit Hanako stands at position init. From position x, she can jump to either position 4*x+3 or 8*x+7 in a single jump. S

TopCoder SRM 628 DIV 2

250-point problem Problem Statement    Janusz is learning how to play chess. He is using the standard chessboard with 8 rows and 8 columns. Both the rows and the columns are numbered 0 through 7. Thus, we can describe each cell using its two coordina

TopCoder SRM 560 Div 1 - Problem 1000 BoundedOptimization &amp; Codeforces 839 E

传送门:https://284914869.github.io/AEoj/560.html 题目简述: 定义"项"为两个不同变量相乘. 求一个由多个不同"项"相加,含有n个不同变量的式子的最大值. 另外限制了每一个变量的最大最小值R[i]和L[i]和所有变量之和的最大值Max. n<=13 题外话: 刚开始做这道题的时候,感觉意外眼熟? codeforces 839 E(此题的退化版):http://codeforces.com/contest/839/pro

Topcoder SRM 648 (div.2)

第一次做TC全部通过,截图纪念一下. 终于蓝了一次,也是TC上第一次变成蓝名,下次就要做Div.1了,希望div1不要挂零..._(:зゝ∠)_ A. KitayutaMart2 万年不变的水题. #include<cstdio> #include<cstring> #include<cmath> #include<iostream> #include<algorithm> #include<set> #include<map&