链接: 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;
}
链接: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;
}