【解题报告】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>c2 c1 的贡献就会被算进期望中。显然 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) 。为了立即发现重复,我们需要一个数组 vis vis[x]=true 表示曼哈顿距离为x的二元组已经出现过了。在 m <script id="MathJax-Element-46" type="math/tex">m</script> 不是很大的情况下,维护这样一个数组的空间也不会超出限制。

    代码

    #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;
    }
    

    (其它题目略)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值