zoukankan      html  css  js  c++  java
  • 【清北学堂2018刷题冲刺】Contest 2

     这场比赛的T1相当智熄。由于至今无法理解题意,我只能解出前20分。诸位dalao谁能比较好地理解题意(独立性)的,请联系我,不胜感激。

     在此本蒟蒻只能贴上题面:

    Task 1:选举

    【问题描述】

     一场选举有三位选手分别编号为1,2,3,还有两位评委4,5。

     评委打分时会给出一个{1,2,3}的排列,表示评委对选手的喜爱程度。例如评委给出一个排列{2,1,3},表示最喜欢2,其次是1,最后是3。

     两位评委将分别给出排列x、y。你需要做的是综合考虑x、y,给出一个最终的排名z。

     由于事先不知道x、y,所以你需要对于每一种不同的x、y,都提前设计好相应的z。(显然x、y有36种情况)

     正式地,你需要设计一个映射{X,Y}-->{Z},其中定义域是评委的排列,值域是你给出的最终排列。(定义域大小是36,值域大小是6,所以映射数量是6^36)。为了方便,用z=f(x,y)表示评委打分是x,y时最终排名是z。

     如果随便给一个映射,可能会被喷,比如两个评委都最喜欢1,你最后却把1排在最后。为了避免这种情况,你的映射可能需要满足几个条件:

     一致性:对于选手a,b,如果两个评委都更喜欢a,那么最终排名中a应当排在b前面。

     独立性:定义函数I(x,a,b),如果排列x中a的位置比b靠前,那么I(x,a,b)=1.否则I(x,a,b)=0。对于选手a,b,考虑评委打分的两种情况(x1,y1)和(x2,y2),如果I(x1,a,b)=I(x2,a,b),并且I(y1,a,b)=I(y2,a,b),那么f(x1,y1)和f(x2,y2)应当满足I(f(x1,y1),a,b)=I(f(x2,y2),a,b)。

     非独裁:如果对于任意的排列x,y,f(x,y)=x,那么称评委4独裁。如果对于任意的x,y,f(x,y)=y,那么称评委5独裁。非独裁就是两个评委都不独裁。

    【输入格式】

     一个数m

    【输出格式】

     一行一个数

     如果m=1,输出共有多少种映射方案(正如题目中所说,方案数是6^36)。

     如果m=2,输出有多少方案满足一致性。

     如果m=3,输出有多少方案满足独立性。

     如果m=4,输出有多少方案满足一致性、独立性。

     如果m=5,输出有多少方案满足一致性、独立性、非独裁。

    【样例输入】

    1
    

    【样例输出】

    10314424798490535546171949056
    

    【数据规模和约定】

     五个点,一个点20分。


    Task 2:游戏

    【问题描述】

     Alice和Bob在玩一个游戏。最初Alice有n颗宝石,Bob有m颗宝石。每一回合,他们会扔一枚硬币,硬币有p的概率正面朝上。

     如果正面朝上,Alice需要给Bob一颗宝石(如果Alice没有宝石了,就不用给了)。否则Bob需要给Alice一颗宝石(如果Bob没有宝石了,就不用给了)。

     如果某个回合结束时,Alice有n颗宝石,那么游戏结束。

     求游戏期望进行多少回合。

    【输入格式】

     第一行两个正整数n,m

     第二行一个有限小数p(小数不超过6位)

    【输出格式】

     一个实数表示答案。

     如果与标准输出的相对误差不超过1e-6就能得分

    【样例输入】

    1 1
    0.5
    

    【样例输出】

    3.00000000
    

    【数据规模和约定】

    • 对于30%的数据, n,m<=1
    • 对于60%的数据, n,m<=10
    • 对于100%的数据, n,m<=100

     我最初的想法是按照回合DP,感觉精度上问题应该不大,赛后经过dummy给的大样例提醒才意识到这样做精度会出现非常大的问题。因为对于很多情况下来讲,期望次数可以非常大,举个例子:对于某组数据,前200000步只占了40%的期望,而剩下60%的期望虽然极其分散,但是其每个值都在200000以上,答案误差可想而知。

     faebdc大佬给出的题解最初让我十分困惑,(dalao的方法太过高级而本蒟蒻脑子又不够)直到今天下午我才得以基本理解这种方法。思路是这样的:

     考虑让进行的回合数趋于无限轮,那么落到每一个点上的期望都会趋于一个固定的值 。

     对于两个相邻点:i和i+1来说,i到i+1的期望是p,i+1到i的期望是1-p,那么二者最终具有的期望比是1-p:p。

     这样我们就可以从0开始递推,到n+m为止,求出从起点走无限轮后,也就是趋于稳态时,到达每一个点的期望。其中可以设0点为单位1,最终再把每个点的值除以总值得到真正的期望。

     想象把走的每一步展开成链,每次都到达一个不同的点,到达终点的期望就是p[n],那么期望步数也就是1/p[n]了。

     时间复杂度O(n),100的范围咋跑都能过。

     当然啦,还有另外一种更容易理解的方法:根据关系构造n元1次方程组,利用高斯消元解方程。但是由于这种方法码量大,速度慢,加上我特别的懒,所以这里就不写了,时间复杂度O(n^3)。

    Code:

    //game 100pts
    #include<cstdio>
    using namespace std;
    
    int n,m;
    long double p,sum=1.0,val[105];
    
    int main(){
    	freopen("game.in","r",stdin);
    	freopen("game.out","w",stdout);
    	scanf("%d%d%Lf",&n,&m,&p);
    	val[0]=1.0;
    	long double v=(1-p)/p;
    	for(int i=1;i<=n+m;i++){
    		val[i]=val[i-1]*v;
    		sum+=val[i];
    	}//get expected percentage after infinity rounds
    	for(int i=0;i<=n+m;++i){
    		val[i]/=sum;
    	}
    	printf("%.10Lf\n",1/val[n]);
    	return 0;
    }
    

    ——————————————————————————————

    Task 3:网络

    【问题描述】

     在计算机科学中,经常要通过分析变量之间的相关性来简化计算过程。变量间的相关性可以用有向图G=(V,E)来表示,图中的点表示变量,边表示变量间的关系。这里G满足:G中的所有边都从编号小的点指向编号大的点。

     从图中选出一个点集T⊆V,如果T中的任意两个点之间都有边(方向是编号小的点指向编号大的点),则称T为团。特别地,空集也认为是一个团。

     如果存在一个点,与T中的任意一个点之间都有边(方向是编号小的点指向编号大的点),那么称T为可扩团。

     如果一个团S不是可扩团,那么称它为极大团。

     给出G,求G有多少个不同的极大团。

     这里G满足一个性质:对于G中任意一个点i,用H[i]表示编号比i小的点中所有与i有边相连的点的集合,那么H[i]是一个团。

    【输入格式】

     第一行n,m,表示点数和边数

     接下来m行,每行两个数a,b,表示有从a到b的边

     注意可能有重边

     保证输入的图满足问题描述中提到的性质。

    【输出格式】

     极大团数量

    【样例输入】

    4 5
    1 2
    1 3
    2 3
    2 4
    3 4
    

    【样例输出】

    2
    

    【数据规模和约定】

    • 对于30%的数据, n<=10
    • 对于60%的数据, n<=1000
    • 对于100%的数据, n,m<=1000000

     这个题目更像是一个结论题。首先你肯定可以得到这样一个简单的推论:

    • 对于每个点i,H[i]是团,那么H[i]+i也一定是团。
    • 所以,极大团一定出自于H[i]+i中。

     接下来问题就在于如何判断H[i]是否为极大团。

     最直观的想法是直接判断。从大的点到小的点遍历,分别遍历它们所属的团并确定其是否为极大团,统计最终答案,时间复杂度O(n2),空间复杂度O(n2),60%的数据绰绰有余。

     但是对于100%的数据,n<=1000000,就比较麻烦了。

     首先,我们需要用链式前向星存图,这就决定了对边的判重要慎重进行,还有就是要处理边的先后顺序,需要一次O(nlogn)的排序,数据范围n是1000000,排序这种做法本身已经有些冒险,那么在输入和循环上就需要稍微卡一点常数保证程序不被卡掉。

     既然数据范围升到了1000000,那么O(n^2)的写法显然不行。经过慎重思考,可以得到这样一个结论:

    • 对于一个团i+H[i],如果它不是极大团,那么一定存在一个点j满足size(H[j])=size(1+H[i]),且i为j团中的最大点。

     根据题目中给出的性质(H[i]一定是团),我们可以知道:只要size(H[j])=size(1+H[i]),而且团j中最大点为i,那么整个团i都会被j包括在内。

     结论考场上不好想,想出来也不好证,所以这个题目就告诉我们两个道理:

    • 暴力大法好
    • 大胆猜想,胡乱证明

     经过上面结论的优化,我们就得到了一份O(n+logn)的代码。注意卡常防止TLE,不要像标程一样丑到1000ms。

    Code:

    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    #define MAXN 1000010
    using namespace std;
    
    bool vis[MAXN];
    int n,m,sz[MAXN],low[MAXN];
    
    inline int max(int x,int y){
    	return x>y?x:y;
    }
    
    struct edge{
    	int u,v;
    	bool operator<(const edge &rhs)const{
    		return u==rhs.u?v<rhs.v:u<rhs.u;
    	}
    }e[MAXN];
    inline int read(){
    	int s=0,w=1;
    	char ch=getchar();
    	while('9'<ch||ch<'0'){
    		if(ch=='-')w=-1;
    		ch=getchar();
    	} 
    	while('0'<=ch&&ch<='9'){
    		s=s*10+ch-'0';
    		ch=getchar();
    	}
    	return s*w;
    }
    int main(){
    	freopen("network.in","r",stdin);
    	freopen("network.out","w",stdout);
    	n=read(),m=read();
    	for(register int i=1;i<=m;++i){
    		e[i].u=read();
    		e[i].v=read();
    	}
    	sort(e+1,e+1+m);
    	int cnt=0;
    	for(register int i=1;i<=m;++i){
    		e[++cnt]=e[i];
    		while(e[i].u==e[i+1].u&&e[i].v==e[i+1].v){
    			++i;
    		}
    	}
    	for(register int i=1;i<=cnt;++i){
    		sz[e[i].v]++;
    		low[e[i].v]=max(low[e[i].v],e[i].u);
    	}
    	for(register int i=n;i>=1;--i){
    		if(sz[low[i]]<=sz[i]-1){
    			vis[low[i]]=true;
    		}
    	}
    	int ans=0;
    	for(register int i=1;i<=n;++i){
    		ans+=!vis[i];
    	}
    	printf("%d",ans);
    	return 0;
    }
    
  • 相关阅读:
    int 类型 占多少字节是由什么决定的
    layer.js漂亮的弹出框
    jquery分页点击后页面置顶
    动态请求页面生成静态页
    弹出ifame页面(jquery.reveal.js)
    Request.QueryString["id"] 、Request.Params["id"] 的强大
    为什么现在改用int.TryParse了
    时间格式(例如:2015-11-09)
    返回前一页
    sql获得某个时间段的数据
  • 原文地址:https://www.cnblogs.com/maomao9173/p/9790665.html
Copyright © 2011-2022 走看看