2020百度之星复赛A+B

文章目录

  • A. Battle for Wosneth
  • B. Binary Addition

A. Battle for Wosneth

链接: hdu6838

Time Limit: 2000/1000 MS (Java/Others)
Memory Limit: 65536/65536 K

Problem Description 你在打游戏的时候碰到了如下问题:

有两个人记作Alice和Bob,Bob的生命值为m,Alice的生命值很高,所以可以认为是无限的。两个人的攻击命中率分别为p%,q%。两个人轮流攻击对方。从Alice开始攻击,每次攻击的时候,如果Alice命中,那么能让对方的生命值减低1,同时自己的生命值能恢复1,如果Bob命中,那么能让对方的生命值减低1,注意Bob不会自己回血。

直到Bob的血量变为0,游戏结束。Alice想知道,游戏结束的时候,自己期望生命值变化是多少,对998244353取模。

注意这里的变化量不是绝对值,也就是如果50%的概率加一,50%的概率减一,那么期望的变化量就是0。

对于一个分数a/b,其中gcd(a,b)=1,那么我们认为这个分数对998244353取模的值为一个数c(0≤c<998244353)满足bc≡a(mod998244353)。

Input 第一行一个正整数T(1≤T≤104)表示数据组数。

对于每组数据,第一行三个整数m,p,q(1≤m≤109,1≤p,q≤100)。

Output 每组测试数据,输出一个数,表示答案。

Sample Input

2
4 100 100
1 50 50

Sample Output

1
499122177

Hint

第一组数据中,每次都能命中,所以Alice能恢复 4 点生命值,减低 3 点生命值,变化量必定为 1。

第二组数据中,对应的分数为 1/2,在Alice命中Bob之前,Bob能期望命中Alice 1/2 次。

由单个a每次攻击伤害期望,算得a的攻击次数.
b的攻击次数比a少一次,再乘以b的每次攻击伤害期望就是a的受到伤害
b受到伤害为m, a回血也就是m,扣血就是a受到的伤害,求差值.
然后,费马小定理求逆元,即ans^(mod-2).

卡住的地方,求的攻击次数为小数时,我竟然想取整!!! 傻逼了
用攻击伤害期望求得的次数,自然也是一种期望,小数是完全可以的

当p 然后师兄告诉我再加个模数就行了…

/*
 *  a回血必然为m,a每次伤害期望为p=p/100,攻击次数m/p.
 *  a先动手,b攻击次数也就是m/p-1,每次期望伤害为q=q/100
 *  ans=m-(m/p-1)*q
 */
#include 
#include 
using namespace std;
typedef long long ll;
const ll mod = 998244353;
ll gcd(ll a, ll b) {
    return b == 0 ? a : gcd(b, a % b);
}
ll qpow(ll a, ll n) {
    ll res = 1;
    while (n) {
        if (n & 1) {
            res = res * a % mod;
        }
        a = a * a % mod;
        n >>= 1;
    }
    return res;
}
int main() {
    int q;
    scanf("%d", &q);
    ll p, q, m;
    while (q--) {
        ll a, b, _gcd;
        scanf("%lld%lld%lld", &m, &p, &q);
        a = 100 * m * (p - q) + p * q;
        b = 100 * p;
        _gcd = gcd(a, b);
        a = a / _gcd % mod;
        b = b / _gcd % mod;
        printf("%lld\n", (a * qpow(b, mod - 2) % mod + mod) % mod);
    }
    return 0;
}

B. Binary Addition

链接:hdu6839

Time Limit: 2000/1000 MS (Java/Others)
Memory Limit: 65536/65536 K (Java/Others)

Problem Description
你有两个无限长01串S,T,分别记作S0S1…和T0T1…。其中S和T从n位之后都是0,也就是当i≥n,有Si=Ti=0。

你可以对S串进行操作:

修改S串的某一位,从0变成1或者从1变成0。
将S当成二进制数加1,也就是记s=∑i≥0Si2^i,将S变成s+1二进制表示的形式,其中低位在最前面。

问最少的步数将S变成T。

Input 第一行一个正整数T(1≤T≤104)表示数据组数。

对于每组数据,第一行一个整数n,接下来两行长度为n(1≤n≤105)的01串S和T,表示S和T的前n位。

保证∑n≤106。

Output 对于每组数据,输出一个整数,表示步数。

Sample Input

3
5
11111
00000
5
10100
01010
5
00000
00001

Sample Output

2
3
1

Hint

第一组数据中,可以选择先加一变成 “000001”,然后将S5变成 ‘0’。

第二组数据中,先加一变为 “01100”,然后直接修改。

第三组数据中,直接修改。

比赛结束后5分钟写出来的,就很气.
后来重新交了一发, 过不了. 又莫名开心了起来,神经病啦

首先, +1操作只能用一次 .
用两次+1操作,最后操作次数必然增多,这里可以想一下.
接下来就简单了,枚举+1操作终点.
然后就是这里我出错了.
如111111,
如果枚举终点在3,那么3就会进一位,而后面是连续1,那么终点跳到了7.
但其实我们可以先将3的1变0,+1后,3再次变成1,后面也不会变动.
这样才符合枚举终点的要求.

/*
 *  贪心,首先要注意到,+1操作只能用一次,再次使用时,肯定会使操作次数变多.
 *  在此基础上,枚举+1操作的终点.
 */
#include 
#include 
using namespace std;
const int N = 1e5 + 10;
int s[N], t[N];
int a[N], b[N];
int main() {
    int T, n;
    scanf("%d", &T);
    while (T--) {
        scanf("%d", &n);
        s[n + 1] = t[n + 1] = 0;
        for (int i = 1; i <= n; i++) {
            scanf("%1d", s + i);
        }
        a[0] = b[0] = 0;
        for (int i = 1; i <= n; i++) {
            scanf("%1d", t + i);
            //a表示直接0->1,1->0操作的次数和
            a[i] = (t[i] != s[i]);
            a[i] += a[i - 1];
            //b表示先加s中0变为1个数,+1操作后s都是0,再加上s中需要变为1的个数
            b[i] = !s[i];
            b[i] += t[i];
            b[i] += b[i - 1];
        }
        int tmp, ans = a[n + 1] = a[n];//假设答案为直接改变的操作数
        //关键终点在n+1,+1操作后s[n+1]可能变为1
        for (int i = 1; i <= n + 1; i++) {
            //关键,若终点在i,但是s[i]为1的话,就需要先变为0提前终止.
            tmp = b[i - 1] + (s[i] == 1) + 1;
            //+1操作后,s[i]为1,若t[i]为0,将s[i]变为0,加上后面直接操作次数
            tmp += a[n + 1] - a[i] + (t[i] == 0);
            ans = min(ans, tmp);
        }
        printf("%d\n", ans);
    }
    return 0;
}

你可能感兴趣的:(2020百度之星复赛A+B)