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代码:

 // BEGIN CUT HERE  

 // END CUT HERE
#line 5 "ScoresSequence.cpp"
#include <vector>
#include <list>
#include <map>
#include <set>
#include <deque>
#include <stack>
#include <bitset>
#include <algorithm>
#include <functional>
#include <numeric>
#include <utility>
#include <sstream>
#include <iostream>
#include <iomanip>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <ctime>
#include <cstring>
using namespace std;
#define N 102
const int INF = 1e9 + ; bool a[N][N];
vector<pair<int, int> > lis, tmp; class ScoresSequence
{
public:
int count(vector <int> s)
{
int n = s.size();
memset(a, , sizeof(a));
for (int i = ; i < n; ++i) a[i][i] = true; lis.clear();
for (int i = ; i < n; ++i) lis.push_back(make_pair(s[i], i)); for (int i = ; i < n - ; ++i)
{
sort(lis.begin(), lis.end());
int c = n - i - lis[].first - ;
for (int k = ; k <= c; ++k)
{
lis[n - i - k].first--;
a[lis[n - i - k].second][lis[].second] = true;
}
for (int k = ; k < n - i - c; ++k)
a[lis[].second][lis[k].second] = true;
tmp.clear();
for (int j = ; j < lis.size(); ++j) tmp.push_back(lis[j]);
lis = tmp;
}
int ans = ;
for (int k = ; k < n; ++k)
for (int i = ; i < n; ++i)
for (int j = ; j < n; ++j)
a[i][j] |= a[i][k] & a[k][j];
for (int i = ; i < n; ++i)
for (int j = ; j < n; ++j)
ans += a[i][j];
return ans;
} // BEGIN CUT HERE
public:
void run_test(int Case) { if ((Case == -) || (Case == )) test_case_0(); if ((Case == -) || (Case == )) test_case_1(); if ((Case == -) || (Case == )) test_case_2(); if ((Case == -) || (Case == )) test_case_3(); if ((Case == -) || (Case == )) test_case_4(); }
private:
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(); }
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; } }
void test_case_0() { int Arr0[] = {, , }; vector <int> Arg0(Arr0, Arr0 + (sizeof(Arr0) / sizeof(Arr0[]))); int Arg1 = ; verify_case(, Arg1, count(Arg0)); }
void test_case_1() { int Arr0[] = {, , }; vector <int> Arg0(Arr0, Arr0 + (sizeof(Arr0) / sizeof(Arr0[]))); int Arg1 = ; verify_case(, Arg1, count(Arg0)); }
void test_case_2() { int Arr0[] = {, , }; vector <int> Arg0(Arr0, Arr0 + (sizeof(Arr0) / sizeof(Arr0[]))); int Arg1 = ; verify_case(, Arg1, count(Arg0)); }
void test_case_3() { int Arr0[] = {, , , , , , , , , }; vector <int> Arg0(Arr0, Arr0 + (sizeof(Arr0) / sizeof(Arr0[]))); int Arg1 = ; verify_case(, Arg1, count(Arg0)); }
void test_case_4() { int Arr0[] = {,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,}; vector <int> Arg0(Arr0, Arr0 + (sizeof(Arr0) / sizeof(Arr0[]))); int Arg1 = ; verify_case(, Arg1, count(Arg0)); } // END CUT HERE }; // BEGIN CUT HERE
int main()
{
ScoresSequence ___test;
___test.run_test(-);
system("pause");
}
// END CUT HERE

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

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

  1. TopCoder SRM 596 DIV 1 250

    body { font-family: Monospaced; font-size: 12pt } pre { font-family: Monospaced; font-size: 12pt } P ...

  2. SRM 719 Div 1 250 500

    250: 题目大意: 在一个N行无限大的网格图里,每经过一个格子都要付出一定的代价.同一行的每个格子代价相同. 给出起点和终点,求从起点到终点的付出的最少代价. 思路: 最优方案肯定是从起点沿竖直方向 ...

  3. 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) [思路] 手速太慢啦,等敲完代码的时 ...

  4. Topcoder SRM 656 (Div.1) 250 RandomPancakeStack - 概率+记忆化搜索

    最近连续三次TC爆零了,,,我的心好痛. 不知怎么想的,这题把题意理解成,第一次选择j,第二次选择i后,只能从1~i-1.i+1~j找,其实还可以从j+1~n中找,只要没有被选中过就行... [题意] ...

  5. 【topcoder SRM 702 DIV 2 250】TestTaking

    Problem Statement Recently, Alice had to take a test. The test consisted of a sequence of true/false ...

  6. Topcoder口胡记 SRM 562 Div 1 ~ SRM 599 Div 1

    据说做TC题有助于提高知识水平? :) 传送门:https://284914869.github.io/AEoj/index.html 转载请注明链接:http://www.cnblogs.com/B ...

  7. TopCoder SRM 560 Div 1 - Problem 1000 BoundedOptimization & Codeforces 839 E

    传送门:https://284914869.github.io/AEoj/560.html 题目简述: 定义"项"为两个不同变量相乘. 求一个由多个不同"项"相 ...

  8. TopCoder SRM 667 Div.2题解

    概览: T1 枚举 T2 状压DP T3 DP TopCoder SRM 667 Div.2 T1 解题思路 由于数据范围很小,所以直接枚举所有点,判断是否可行.时间复杂度O(δX × δY),空间复 ...

  9. Topcoder SRM 648 (div.2)

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

随机推荐

  1. JBoss 系列十七:使用JGroups构建块MessageDispatcher 构建群组通信应用

    内容概要 本部分说明JGroups构建块接口MessageDispatcher,具体提供一个简单示例来说明如何使用JGroups构建块MessageDispatcher 构建群组通信应用 示例描述 构 ...

  2. JavaScript | 模拟文件拖选框样式 v1.0

    ————————————————————————————————————————————————————————— 文件拖选v1.0 图片不清楚时请右键点击"在新链接中打开图片" ...

  3. Visual studio之C#的一些常见问题

    背景 要写一个APP,APP通过串口控制下位机,在此记录C#的一些常用控件的使用办法. 正文 单击button控件,执行对应操作. 选择要操作的button控件,在属性栏内点击类似闪电标志一样的事件, ...

  4. case when then 中的条件里面加上 and or 的处理方式

    错误的方式: end 正确1: end 正确2,去掉括号: end

  5. android studio教学视频资源(点开即看)

    android studio教学视频资源(点开即看) 自从Google推出android studio之后.包含github在内的非常多第三方代码库项目很多其它的採用的android studio编译 ...

  6. jQuery基本选择 元素

    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <hea ...

  7. MQTT Client软件-MQTTBox

    最近发现了一个连接mqtt broker的软件:MQTTBox.GitHub地址:https://github.com/workswithweb/MQTTBox 官网网站的介绍为:使用MQTTBox增 ...

  8. 安装Spring+搭建Spring开发环境

    https://blog.csdn.net/csdnsjg/article/details/80152815 https://jingyan.baidu.com/article/219f4bf798e ...

  9. mysql-multi source replication 配置

    1.关键步骤 change master to master_host='172.16.192.201', master_port, master_user='repl', master_passwo ...

  10. NGUI的一个bug记录

    在当前帧内触发按钮事件,然后把click事件的list删去,再添加,这时候会再次触发. 不确定,先记一下