【解題報告】2016 Multi-University Training Contest 3

題目鏈接


A - Sqrt Bo(HDU 5752)

大意

定義 f(x) 的值爲 x 開方後向下取整的結果。題給一個整數 n ,問滿足 fy(n)=1 的最小的 y 是多少,如果這個 y 小於 5 則將它輸出,否則輸出TAT。

思路

n 值大到一定程度時,一定沒法通過 5 次迭代得到 1 。我們將這個值算出來並將它賦值給upperBound。每當輸入一個 n 個時候(注意要以字符串形式輸入)我們就可以判斷n與upperBound的關係,若比它大的話直接輸出TAT(當心 n<1 的情況)。否則可以暴力迭代(不超過 5 次)算出結果。

代碼

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
const ll upperBound = 4294836224LL;
ll a;
string s;
stringstream ss;

int main() {
    while(cin >> s) {
        if(s.size() > 10) {
            puts("TAT");
            continue;
        }
        ss.clear();
        ss << s;
        ss >> a;
        if(a < 1 || a > upperBound) {
            puts("TAT");
            continue;
        }
        bool ok = false;
        for(int i = 0; i <= 5; i++) {
            if(a == 1) {
                cout << i << endl;
                ok = true;
                break;
            }
            a = (ll)floor(sqrt(1.0 * a));
        }
        if(ok == false) {
            puts("TAT");
        }
    }
    return 0;
}

B - Permutation Bo(HDU 5753)

大意

題給一個序列 c ,另規定 1n 的排列爲 h 。定義 f(h)=ni=1ci[hi>hi1andhi>hi+1] 。求 f(h) 的期望值。

思路

根據本題的數據規模來看,肯定無法枚舉排列然後算期望了。一個可行的思維是算出序列 c 中每個元素對期望的貢獻。

  • 當考慮 c 的第 1 個元素 c1 時,我們只要考慮前兩個元素的排列,當前兩個元素呈現出 c1>c2c1 的貢獻就會被算進期望中。顯然 c1>c2 的概率是 12
  • 當考慮 c 的第 2 個元素 c2 時,我們只要考慮前三個元素的排列,當前三個排列呈現出 c1<c2,c3<c2 時, c2 的貢獻就回被算進期望中。顯然這種排列的概率是 13
  • 3 到第 n1 個元素的貢獻與第 2 個元素的貢獻相似。第 n 個元素的貢獻與第 1 個元素的貢獻相似。

於是我們枚舉每個元素,將其貢獻加入期望中即可。

代碼

#include <bits/stdc++.h>
using namespace std;

int n, a;
double ans;

int main() {
    while(~scanf("%d", &n)) {
        ans = 0;
        for(int i = 1; i <= n; i++) {
            scanf("%d", &a);
            if(i == 1 || i == n) {
                ans += 1.0 * a / 2;
            }
            else {
                ans += 1.0 * a / 3;
            }
        }
        if(n == 1) {
            printf("%.5f\n", 1.0 * a);
        }
        else {
            printf("%.5f\n", ans);
        }
    }
    return 0;
}

C - Life Winner Bo(HDU 5754)

大意

有兩個人在國際象棋棋盤上用國際象棋一個棋子進行博弈,這個棋子可能是王,王后,城堡和騎士。棋子從座標 (1,1) 出發,兩個人輪流讓棋子按照國際象棋規則走一次,先讓棋子到達 (N,M) 的人獲勝。問先手和後手誰是贏家。

思路

根據國際象棋棋盤的特點,該遊戲可以抽象成從兩個石子堆(分別有 N1,M1 個石子)中拿石子,先將所有石子拿完的人獲勝的博弈模型。根據四個棋子的走法不同,抽象模型中拿石子的方法也會有所不同。下面就分棋子進行討論。

  • 王:由於王可以在一次移動中橫向,縱向或斜向移動一步,因此對應到拿石子模型中,當兩堆石子的數量 N1,M1 都是偶數的時候當前狀態是必敗態(否則一定可以通過一次行動使得當前兩堆石子的數量變成偶數)。
  • 城堡:由於城堡可以在一次移動中橫向或者縱向移動任意步,因此對應到拿石子的模型中,與 NimGame 模型是一樣的。因此只有 N=M 的時候先手失敗,其餘情況下是先手獲勝(先手總可以令對方面對兩堆石子數目相同的情況,這種情況是必敗態)。
  • 騎士:由於騎士可以在一次移動中橫移兩步縱移一步或橫移一步縱移兩步,因此對應到拿石子模型中,相當於在 N1,M1 兩堆石子中一堆拿一個,而另一堆拿兩個。當 N1+M1 不是 3 的倍數的時候一定是平局。當它是 3 的倍數的時候,當 N=M 時一定是必敗的,因爲只要想脫離必敗態(例如兩堆石子分別 12 )就一定會被趕回必敗態(例如兩堆石子分別 21 )。若 NM 不等且它們的差值爲 1 時一定是必勝的,因爲必定可以用一步把對手逼入必敗態。
  • 王后:由於王后可以在一次移動中橫向,縱向或斜向移動任意步,因此對應到拿石子的模型中,與 WythoffGame 模型是一樣的。因此可以用公式 ai=i×1+5/2bi=ai+i 檢驗當前局勢 (N1,M1) 是否是必敗局勢。

代碼

#include <bits/stdc++.h>
using namespace std;

const int maxn = 1000;
char ch[] = {'G', 'D', 'B'};
int t, type, n, m, ans, G[maxn+5][maxn+5];

int king() {
    return n % 2 == 1 && m % 2 == 1 ? -1 : 1;
}

int castle() {
    return n == m ? -1 : 1;
}

int knight() {
    if(n > m) {
        swap(n, m);
    }
    if(n == m && (n - 4) % 3 == 0 && n >= 4) {
        return -1;
    }
    if(n % 3 == 2 && m % 3 == 0 && m - n == 1) {
        return 1;
    }
    return 0;
}

int queen() {
    if(n > m) {
        swap(n, m);
    }
    int a = n - 1, b = m - 1, k = m - n;
    double tmp = (1.0 + sqrt((double)5.0)) / 2.0;
    return (int)(k * tmp) == a ? -1 : 1;
}

int main() {
    scanf("%d", &t);
    while(t--) {
        scanf("%d%d%d", &type, &n, &m);
        if(type == 1) {
            ans = king();
        }
        if(type == 2) {
            ans = castle();
        }
        if(type == 3) {
            ans = knight();
        }
        if(type == 4) {
            ans = queen();
        }
        printf("%c\n", ch[ans+1]);
    }
    return 0;
}

J - Rower Bo(HDU 5761)

大意

主人公要駕船從平面直角座標系中的 (0,a) 去到 (0,0) 。主人公的船相對水流的速度大小恆爲 v1 ,方向始終朝向 (0,0) 。河流有一個垂直於 y 軸的速度 v2 。問主人公從出發到目的地要花多少時間。

思路

再思考無果的時候,不妨將問題轉化一下:主人公從 (0,a) 出發,目的地以 v2 的速度向x軸正向移動,而水流是靜止的。主人公始終以 v1 的速度朝向目的地移動,問主人公到達目的地的時間。
設T爲主人公要花的時間,另外主人公在 t 時刻的速度與 x 軸方向的夾角爲 θ(t) 。我們考慮主人公在水平和豎直方向上方向上的運動情況,對水平方向有

T0v1cosθ(t)dt=v2T

對豎直方向有

T0v1sinθ(t)dt=a

此時還有一個條件沒有利用,那就是主人公的速度方向始終朝向終點,那麼對主人公當前位置到目的地的方向有

T0(v1v2cosθ(t))dt=a

最後聯立這三個方程可得

T=v1av21+v22

於是原問題得解。

代碼

#include <bits/stdc++.h>
using namespace std;

int a, v1, v2;

int main() {
    while(~scanf("%d%d%d", &a, &v1, &v2)) {
        if(a == 0) {
            puts("0.00000");
        }
        else if(v1 <= v2) {
            puts("Infinity");
        }
        else {
            printf("%.5f\n", 1.0 * v1 * a / (v1 * v1 - v2 * v2));
        }
    }
    return 0;
}

K - Teacher Bo(HDU 5762)

大意

題給 n 個點 (x,y) ,並約定 0x,ym 。問是否能從這些點鐘找出一個四元組 (A,B,C,D) ,使得二元組 (A,B) 與二元組 (C,D) 的曼哈頓距離相等。(這兩個二元組不能相同)

思路

雖然看上去不同的二元組會很多,但是根據鴿巢原理,當 n(n1)2>2m ,所求的四元組是一定能夠被找到的,因此即使我們暴力枚舉所有的二元組,複雜度也將是 min(n(n1)2,2m) 。爲了立即發現重複,我們需要一個數組 visvis[x]=true 表示曼哈頓距離爲x的二元組已經出現過了。在 m 不是很大的情況下,維護這樣一個數組的空間也不會超出限制。

代碼

#include <bits/stdc++.h>
using namespace std;

const int maxn = 1e5 + 10;
bool ok;
int t, n, m, dx, dy, mht, x[maxn], y[maxn], vis[maxn];

int main() {
    scanf("%d", &t);
    while(t--) {
        scanf("%d%d", &n, &m);
        for(int i = 1; i <= n; i++) {
            scanf("%d%d", &x[i], &y[i]);
        }
        memset(vis, 0, sizeof(vis));
        ok = false;
        for(int i = 1; i <= n; i++) {
            for(int j = i + 1; j <= n; j++) {
                dx = abs(x[i] - x[j]);
                dy = abs(y[i] - y[j]);
                mht = dx + dy;
                if(vis[mht] == true) {
                    ok = true;
                    i = n + 1;
                    break;
                }
                vis[mht] = true;
            }
        }
        puts(ok ? "YES" : "NO");
    }
    return 0;
}

(其它題目略)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章