【清北学堂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;
}