[CF 332D]Theft of Blueprints题解中数学结论证明的翻译
2015-03-30 18:58
411 查看
题目:
有一个N<=2000个顶点的边带权简单无向图G。满足:对于任意k个顶点(1<=k<N)组成的集合S,都有唯一顶点u和S中所有顶点相邻。(*)
令集合S的权值为u和S中所有顶点边的权值之和。随机选取集合S,求期望权值。
题解:
这道题主要基于如下结论:当k>=3时,G是一个含k+1个顶点的完全图。(①)
①的证明仅见于俄文版的官方题解。我们在此将其翻译成中文。(其实是先用google把毛文翻成英文然后再手工把英文翻成中文,哈哈)
证明:
引理1.
图中任意两个不同的顶点恰好有k-1个公共相邻顶点.引理1的证明:
考虑任意两个不同的顶点s,t。令它们的全部公共相邻顶点为v1,v2,...,vl。如果l>=k,则集合{v1,...,vk}有两个公共相邻顶点s,t,和(*)矛盾。故l<=k-2.考虑顶点集合S={s,t,v1,...,vl},它包含l+2<=k个元素。如果l+2<k,我们可以用S之外任意的顶点将这个集合扩充到含k个元素。设扩充后的集合为T。根据(*),存在唯一顶点u∉T,使得u和T中所有顶点相邻。特别地,u和s,t均相邻。但根据集合T的定义,T中包含了所有和s,t均相邻的顶点,和u∉T矛盾。因此l=k-1.引理2.
图G包含一个含k+1个顶点的完全子图。引理2的证明:
考虑集合S={v1,...,vk,vk+1},其中v1,...,vk是图中任意的互异顶点,vk+1是它们的公共相邻顶点。基于集合S,我们给出一个能得到引理2中完全子图的方法:运行k-1次迭代,第i次我们考虑顶点vi。如果vi和vi+1,vi+2,...,vk均相邻,就进入下一次迭代。否则,考虑集合T={v1 , ... , vi-1 , vi+1 , ... , vk+1}.根据(*),有一个顶点u和T中所有顶点均相邻(显然u≠vi,因为vi不和j中所有顶点相邻)。我们用u代替vi,进入下一次迭代。显然,在最后一次迭代结束后,我们就得到了一个完全子图,它包含的顶点集合为S。
回到原题目。
下面我们证明对于k>=3,图G是一个含k+1个顶点的完全图。根据引理2,在图G中有一个完全子图,其顶点集合为S={v1,...,vk+1}。我们假设图G中存在一个顶点u∉S。由于S是一个完全图的顶点集合,且由引理1,任意两个顶点恰有k-1个公共相邻顶点,因此对于S中任意两个互异顶点v1,v2,它们的公共相邻顶点均在S中。由于任意k个顶点都恰有一个公共相邻顶点,所以任意i(2<=i<=k-1)个顶点均至少有一个公共相邻顶点。特别地,由于k>=3,故顶点v1,v2,u有一个公共相邻顶点,且该顶点在S中(因为v1,v2的公共相邻顶点均在S中)。若u和S中的两个顶点x,y相邻,则与引理1矛盾(此时x,y有k个公共相邻顶点)。因此,S中存在唯一一个顶点x和u相邻。取S中不同于x的顶点y,则u,x,y三个顶点有一个公共相邻顶点,且它在S中。但这就意味着u和S中的两个顶点相邻,矛盾。
故G中所有点均属于S,G是一个含k+1个顶点的完全图,结论成立。
有了这个结论就很好做了:显然类似C(N,K)这样的组合数都不会太大,所以直接算每个点作为“公共相邻顶点”对答案的贡献即可。时间复杂度O(N^2).
AC代码见下:
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> #include<iomanip> using namespace std; typedef long long LL; typedef long double LDB; const int SIZEN=2010; LDB C(int n,int m){ LDB ans=1; for(int i=0;i<m;i++){ ans*=n-i; ans/=m-i; } return ans; } int N,K; LL c[SIZEN][SIZEN]={0}; void work(void){ LDB ans=0; for(int i=1;i<=N;i++){ int snum=0; LDB ssum=0; for(int j=1;j<=N;j++){ if(c[i][j]!=-1){ snum++; ssum+=c[i][j]; } } if(snum>=K){ ans+=C(snum-1,K-1)*ssum; } } ans/=C(N,K); cout<<(LL)(ans+1e-6)<<endl; } void read(void){ scanf("%d%d",&N,&K); for(int i=1;i<=N;i++){ c[i][i]=-1; for(int j=i+1;j<=N;j++){ scanf("%I64d",&c[i][j]); c[j][i]=c[i][j]; } } } int main(){ //freopen("t1.in","r",stdin); read(); work(); return 0; }
相关文章推荐
- 用C证明一个数学结论
- cf 11B Jumping Jack(贪心,数学证明一下,,)
- 【题解】n个数最大公约数总和,m组人坐一排同一组的不能相邻,CF #300 F
- 一个高中数学不等式的证明
- leetcode26题 题解 翻译 C语言版 Python版
- CF - 817C. Really Big Numbers - 二分+数学
- 算法:字符串消除问题的数学证明
- 数学·Euler函数·证明及其求法
- 常用数学证明方法
- [动态规划+数学结论]HOJ_12301_Eleven Lover
- 数学定理证明机械化的中国学派(I)
- 10大反直觉的数学结论
- 数学证明:人类有95%的概率在近800年内灭亡
- 随机洗牌算法之数学证明
- 【结论&例题】【数学】NKOJ3813 最多因数
- 数学词汇英语翻译
- kernel 内存分配伙伴算法数学证明
- AdaBoosting流程及数学证明
- 数学常用公式及规律、结论(一)
- 2014年数学建模美赛题目原文及翻译