【清北學堂2018-刷題衝刺】Contest 2
阿新 • • 發佈:2018-11-12
這場比賽的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(n^2),空間複雜度O(n^2),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;
}