1. 程式人生 > >[20180815]校內模擬賽

[20180815]校內模擬賽

efi cpp 開心 變量 遊戲 復雜度 復制 span num

T1 遊戲(game)


問題描述

Alice準備和Bob玩一個遊戲,他們先拿出若幹堆石子,每一堆裏面都有一定數量的石子。

Alice和Bob輪流操作,Alice先手,每次操作需要選擇一堆石子數量大等於2的石子,把這堆石子分成兩堆。

假設這堆石子中有x個石子,那麽可以分成一堆y(1≤y<x)個石子和一堆x-y個石子。

如果輪到一個人操作時沒有可選的石子堆,這個人就輸了。

Alice有n堆石子,其中第i堆有\(a_i\)個石子,他打算選出其中連續一段石子跟Bob玩。

你需要回答m次詢問,每次查詢取出第l堆到第r堆石子進行遊戲,雙方都選擇最優策略時誰會獲勝。


輸入格式

第一行兩個正整數n,m。

第二行n個正整數,表示\(a_i\)

接下來m行,每行兩個正整數l,r,表示一個詢問。


輸出格式

對於每個詢問輸出一行“Alice”或“Bob” ,表示答案。


樣例

樣例輸入


2 3
1 2
1 1
2 2
1 2

樣例輸出


Bob
Alice
Alice

數據範圍

對於 100%的數據,n,m,\(a_i\) ≤ 10^5 ,l ≤ r。


Solution

一個大小為n的石子堆可以被分(n-1)次。

對於一段石子,如果能分k次:

  1. k是奇數,Alice勝
  2. k是偶數,Bob勝

前綴和處理。


#include<iostream>
#include<cstdio>
inline long long read(){
    long long x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')ch=getchar();ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
    return x*f;
}
#define MN 100005
long long n,m,a[MN];
int main(){
    freopen("game.in","r",stdin);
    freopen("game.out","w",stdout);
    n=read(),m=read();
    register int i,x,y;
    for(i=1;i<=n;i++) a[i]=read()-1+a[i-1];
    while(m--){
        x=read();y=read();
        if((a[y]-a[x-1])&1) puts("Alice");
        else puts("Bob");
    }
    return 0;
}




T2 數字(number)


問題描述

小 D 最近在研究 A+B 問題,可是這個問題對他來說太棘手了,因為他連讀入都不會。

小 D 開了兩個變量a和b,並且把它們的初值設為1.

接下來他可以添加若幹行代碼, 每一行可以是\(a = a + b\)\(b = a + b\)

已知A+B 問題的樣例輸出是n,小D 想知道自己至少需要添加多少行代碼才能讓a和b中至少有一個等於n以通過樣例呢?


輸入格式

一行一個正整數n。


輸出格式

輸出一個整數,表示答案。


樣例


樣例輸入


5


樣例輸出


3


數據範圍

對於 100%的數據,n≤ 10^6 。


Solution

枚舉最後一步是由那兩個數相加得到的,設為A和B。

可以發現倒推回去的過程類似求gcd的過程。

顯然A和B必須要互質。

在倒推的過程中計算步數,最後取個min。


#include<iostream>
#include<cstdio>
inline long long read(){
    long long x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')ch=getchar();ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
    return x*f;
}
#define MN 1000005
int n,m;
int ans=MN,N;
inline int gcd1(int x,int y){
    if(y==0) return 0;
    return gcd1(y,x%y)+(x/y);
}
inline int gcd2(int x,int y){
    if(y==0) return x;
    return gcd2(y,x%y);
}
int main(){
    freopen("number.in","r",stdin);
    freopen("number.out","w",stdout);
    n=read();register int i;
    for(i=1;i<=n-i;i++){
        m=gcd2(n-i,i);if(m!=1) continue;
        ans=std::min(ans,gcd1(n-i,i));
    }
    printf("%d\n",ans);
    return 0;
}




T3 旅行(travel)


問題描述

旅行家小C今天在一條數軸上旅遊,一開始他位於x。

數軸王國接下來會依次舉行n次活動,每次在區間\([l_i,r_i]\)內舉行。

在每個活動開始前,小C可以移動任意的距離,從a移動到b會讓他積攢|a-b|的疲勞值。

如果一個活動開始時,小C不在活動範圍內,他就會不開心,並且如果離活動範圍越遠他就越不開心

具體地說,如果小C當前位置到活動範圍的最短距離為k,小C就會積攢k的疲勞值。

請你求出所有活動結束後小C最小的疲勞值之和。


輸入格式

第一行兩個正整數n,x。

接下來n行,每行兩個正整數\(l_i\) ,\(r_i\)


輸出格式

輸出一個整數,表示答案。


樣例

樣例輸入


5 4
2 7
9 16
8 10
9 17
1 6


樣例輸出


8


數據範圍

對於 100%的數據,n ≤ 5* 10^5 ,x,\(l_i\) ,\(r_i\) ≤ 10^9 。


Solution

維護使答案最優的區間[L,R],初始L=R=x。

設d(X,i)為X位置到活動[li,ri]的最小距離。

假設當前的位置為pos,對於一個活動[li,ri]

(1) pos不在活動區間內

只要不往遠離這個活動區間的方向走,或者的走到區間裏面去,疲勞值總是一定的,就是d(pos,i)。

  1. 如果往遠離這個活動 區間的方向走,會使疲勞值d(pos,i)的基礎上在增加走的距離,可以把它看作先原地不動,活動i結束後再走相應的距離,疲勞值不變。
  2. 如果走到區間裏面去,同樣也可以先走到活動的邊界,剩下的距離留到活動結束後再走。

所以使答案最優的區間:[pos,li][ri,pos]

同樣的,如果當前pos的區間為[L,R],且[L,R][li,ri]無交,[L,R]應更新為[min(R,ri),max(L,li)]

(2)pos在活動區間內

原地不動會是最優的,這應該比較顯然。

同樣的,如果當前pos的區間為[L,R],且[L,R][li,ri]相交,[L,R]應更新為原先兩個區間的交集,即

[max(L,li),min(R,ri)].

以下是學長的題解:

f[i][j]表示第i個活動後在j的最小疲勞值,對於每個i,先從i-1復制DP值,接下來有兩部分計算,第一部分算活動的疲勞值,j<li的加上li-j,j>ri的加上j-ri。第二部分移動,用f[i][j]+1更新f[i][j-1]f[i][j+1]

事實上,對於每個i,把f[i][j]看成關於j的函數,這個函數會由最多三部分組成,第一部分形如y=-x+a,第二部分y=b,第三部分y=x+c,也就是差分恰好形成-1,0,1三段.

考慮用歸納法證明:i=0 f[i][j]=|j-x|,顯然滿足。i增大時,第一部分計算會讓函數再加上一個差分為-1,0,1的函數,差分會變成-2,-1,0,1,2。第二部分計算會讓函數的差分絕對值不超過1,差分又會變成-1,0,1。差分為0的那一段就是最小的答案,維護這一段的位置並順便計算答案即可。

時間復雜度O(n)


#include<iostream>
#include<cstdio>
inline long long read(){
    long long x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')ch=getchar();ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
    return x*f;
}
#define MN 500005
int n,pos,l,r,L,R;
long long ans;
bool cross(int x,int y,int a,int b){
    long long len=(y-x)+(b-a);
    x=std::min(x,a);
    y=std::max(y,b);
    return len>(y-x);
}
int main(){
    freopen("travel.in","r",stdin);
    freopen("travel.out","w",stdout);
    int n=read();
    pos=L=R=read();ans=0;
    for(int i=1;i<=n;i++){
        l=read(),r=read();
        if(cross(l,r,L,R)){
            L=std::max(l,L);
            R=std::min(r,R);
        }
        else{
            R=std::min(R,r);
            L=std::max(L,l);
            std::swap(L,R);
            ans+=R-L;
        }
    }
    printf("%lld\n",ans);
    return 0;
}





Blog來自PaperCloud,未經允許,請勿轉載,TKS!

[20180815]校內模擬賽