1. 程式人生 > >【清北學堂2018-刷題衝刺】Contest 2

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