A. Battle for Wosneth (Hdu 6838)
題目大意
初始\(Alice\)有無限血,\(Bob\)有\(m\)滴血。\(Alice\)有\(p\%\)命中\(Bob\),並使\(Bob\)減少一滴血,自身回復一滴血。\(Bob\)有\(q\%\)概率命中\(Alice\),並使\(Alice\)減少一滴血,但自身血不變。問當\(Bob\)血量減少為\(0\)時,\(Alice\)的期望血量變化值是多少。結果對\(998244353\)取模。
解題思路
當\(Bob\)血量大於\(1\)時,設\(Alice\)命中一次\(Bob\),自身血量變化的期望值為\(x\),則(此處\(p,q\)為小數)
解得
所以Bob從\(m\)滴血扣到\(1\)滴血時,Alice的血量變化期望值為
當\(Bob\)剩下一滴血時,由於如果\(Alice\)命中他,則Bob不會反擊,這是與上方的區別所在,設\(Alice\)命中Bob,自身血量變化值為\(y\),則
解得
所以最終答案
這可以理解為先假設\(m\)輪,\(Bob\)都會反擊,造成變化期望值為\((1 - \dfrac{q}{p}) \times m\),再減去最后一次\(Bob\)實際反擊的變化(扣血)期望\(-\dfrac{p}{1 - (1 - p)} \times q = -q\)。
即為
神奇的代碼
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL mo = 998244353;
LL qpower(LL a, LL b)
{
LL qwq = 1;
while (b)
{
if (b & 1)
qwq = qwq * a % mo;
b >>= 1;
a = a * a % mo;
}
return qwq;
}
LL inv(LL x)
{
return qpower(x, mo - 2);
}
int main(void)
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int kase;
cin >> kase;
for (int ii = 1; ii <= kase; ii++)
{
LL m, q, p;
cin >> m >> p >> q;
p = p * inv(100) % mo;
q = q * inv(100) % mo;
LL ans = ((1 - q * inv(p) % mo + mo) % mo * m % mo + q) % mo;
cout << ans << endl;
}
return 0;
}
B. Binary Addition (Hdu 6839)
題目大意
給你一串無限長的\(01\)串\(S、T\),其中第\(n+1\)位及以后都是\(0\)。現你有兩種操作作用於\(S\)串:
- 將某一位與\(1\)異或
- 將其視為一個數,對它加一。其中最低位在最左邊
求最小的操作次數,使得\(S\)串變成\(T\)串。
解題思路
這種看似麻煩的題要去想想特別之處。
可以證明猜測操作二要執行則僅可能執行一次。
操作二有什么用?
如果第一位是\(0\),操作二與操作一沒區別。
如果第一位是\(1\),操作二就能夠將前面一連串的\(1\)變成\(0\),在這之后的\(0\)變成\(1\)
如果我們會執行兩次操作二,由於執行了第一次操作二,前面的數變成了\(0\),我們要重新變成\(1\),才能再執行操作二。而這最終的結果也只是把某一位變成\(1\),而這一結局采用操作一可以一步到位。
所以我們就得到了個重要性質:操作二只能執行一次或者不執行
所以,我們就枚舉操作二的執行效果,即枚舉\(i\),把前\(i\)個數變成\(1\),並把第\(i+1\)個數變成\(0\),然后執行一次操作二,剩下的全部執行操作一即可。
設\(num_0[i]\)表示\(S\)串的前\(i\)個數中\(0\)的個數,\(num_1[i]\)表示\(T\)串的前\(i\)個數中\(1\)的個數,\(cnt[i]\)表示\(S\)和\(T\)串的\([i..n]\)中不同的數的個數。
則此時的次數就為\(num_0[i] + (s[i+1] == 1) + 1 + num_1[i] + (t[i+1] == 0) + cnt[i+2]\)
對所有\(i\)以及\(cnt[1]\)取最小值即是答案。
神奇的代碼
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
template <typename T>
void read(T &x)
{
int s = 0, c = getchar();
x = 0;
while (isspace(c))
c = getchar();
if (c == 45)
s = 1, c = getchar();
while (isdigit(c))
x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
if (s)
x = -x;
}
template <typename T>
void write(T x, char c = ' ')
{
int b[40], l = 0;
if (x < 0)
putchar(45), x = -x;
while (x > 0)
b[l++] = x % 10, x /= 10;
if (!l)
putchar(48);
while (l)
putchar(b[--l] | 48);
putchar(c);
}
const int N = 1e5 + 8;
char s[N], t[N];
int n;
int cnt[N];
int num0[N];
int num1[N];
int qwq(int pos)
{
return num0[pos] + (s[pos + 1] == 1) + 1 + num1[pos] + (t[pos + 1] == 0) + cnt[pos + 2];
}
int main(void)
{
int kase;
read(kase);
for (int ii = 1; ii <= kase; ii++)
{
read(n);
scanf("%s", s + 1);
scanf("%s", t + 1);
num0[0] = num1[0] = 0;
for (int i = 1; i <= n; ++i)
{
s[i] -= '0';
t[i] -= '0';
num0[i] = num0[i - 1] + (s[i] == 0);
num1[i] = num1[i - 1] + (t[i] == 1);
}
cnt[n + 1] = 0;
cnt[n + 2] = 0;
bool sign = false;
int cur = n;
for (int i = n; i >= 1; --i)
{
cnt[i] = cnt[i + 1] + (s[i] ^ t[i]);
}
int ans = cnt[1];
for (int i = 1; i <= n; ++i)
{
ans = min(ans, qwq(i));
}
write(ans, '\n');
}
return 0;
}
C. Range k-th Maximum Query (Hdu 6840)
題目大意
給定一個\(n\)個數的序列,以及正整數\(k,l\),要求對它重新排序,使得所有長度為\(l\)的子區間的第\(k\)大的數和和最大和最小。求最大值和最小值。
解題思路
我們將數列從大到小排列。前\(k-1\)大的數不會對答案有貢獻。
要讓和最大,我們期望大的數對答案的貢獻盡可能多。
於是我們可以構造這樣的序列,它是由若干個長度為\(l\)的區間構成。
每個這樣的區間,前\(l - k\)個位置標記為紅,后\(k\)個位置標記為藍。
我們將這個排好序的序列,從左到右,按順序填充藍的位置,放完藍的,然后再從右到左,按順序填充紅的位置。
最后一個長度不足\(l\)的區間(如果有的話),前\(l - k\)個位置標記為紅,后\(k\)個位置標記為藍(如果有的話)。
這樣就是最大值的構造。
最小值,將數列從小到大排列,前\(k\)大的數也就是說前\(l - k + 1\)小的數,再按照上面構造就可以了。
既然構造出來了,答案自然也就能求出來了。
最大值的情況,設\(d = \lfloor \dfrac{n}{l} \rfloor , r = n \% l\)
排好序的數列里,\(k 到 d * k - 1\)的數都對答案有\(1\)次的貢獻,其中位置是\(k\)的倍數的還有額外的\((l - k)\)次的貢獻。
最后第\(d * k\)位的貢獻次數跟\(r\)有關
如果\(r > (l - k)\),則第\(d * k + 1\)位到第\(d * k + r - (l - k)\)的數對答案也有\(1\)次的貢獻。
神奇的代碼
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
template <typename T>
void read(T &x)
{
int s = 0, c = getchar();
x = 0;
while (isspace(c))
c = getchar();
if (c == 45)
s = 1, c = getchar();
while (isdigit(c))
x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
if (s)
x = -x;
}
template <typename T>
void write(T x, char c = ' ')
{
int b[40], l = 0;
if (x < 0)
putchar(45), x = -x;
while (x > 0)
b[l++] = x % 10, x /= 10;
if (!l)
putchar(48);
while (l)
putchar(b[--l] | 48);
putchar(c);
}
const int N = 1e5 + 8;
int n, k, l, d, r;
LL a[N];
LL solve(LL a[], int k)
{
LL ans = 0;
for (int i = k; i < d * k; ++i)
{
ans += a[i] * (1 + (l - k) * (i % k == 0));
}
ans += a[d * k] * min(r + 1, l - k + 1);
if (r > l - k)
{
for (int i = d * k + 1, yu = r - l + k; yu; ++i, --yu)
{
ans += a[i];
}
}
return ans;
}
int main(void)
{
int kase;
read(kase);
for (int ii = 1; ii <= kase; ii++)
{
read(n);
read(l);
read(k);
for (int i = 1; i <= n; ++i)
{
read(a[i]);
}
sort(a + 1, a + 1 + n, greater<int>());
d = n / l;
r = n % l;
LL ans1 = solve(a, k);
sort(a + 1, a + 1 + n);
k = l - k + 1;
LL ans2 = solve(a, k);
printf("%lld %lld\n", ans1, ans2);
}
return 0;
}
D. Link Cut Tree (Hdu 6841)
題目大意
qwq
解題思路
qwq
神奇的代碼
qwq
E. Battle for Wosneth2 (Hdu 6842)
題目大意
初始\(Alice\)有\(n\)滴血,\(Bob\)有\(m\)滴血。\(Alice\)有\(p\%\)命中\(Bob\),並使\(Bob\)減少一滴血,但自身血不變;\(Bob\)有\(q\%\)概率命中\(Alice\),並使\(Alice\)減少一滴血,但自身血不變。當一方血量減為\(0\)時,對方獲勝。問\(Alice\)獲勝的概率。答案對\(998244353\)取模。
解題思路
我們抽象成一個二維平面圖,左下角\((0,0)\),初始位於\((n,m)\),然后有三個移動方向,問移動到\((r,0)\)的概率是多少(\(r\)是任意一個不大於\(n\)的數)。
當前位置為\((i,j)\)
-
移動到\((i-1, j-1)\)的概率\(a = \dfrac{pq}{1 - (1 - p)(1 - q)}\)
-
移動到\((i, j - 1)\)的概率\(b = \dfrac{p(1-q)}{1 - (1 - p)(1 - q)}\)
-
移動到\((i - 1, j)\)的概率\(c = \dfrac{q(1-p)}{1 - (1 - p)(1 - q)}\)
值得注意的是,從\((i,1)\)移動到\((i,0)\)的概率是\(d = \dfrac{p}{1 - (1 - p)(1 - q)}\)
所以我們先計算移動到\((r,1)\)的概率,最后再計算移動到\((r,0)\)的概率。
這里有兩個自由變量。
如果我們假設移動到\((r,1)\),或者說,水平方向進行了\(i = n - r\)次移動,還要假設,我們進行了\(x\)次情況一的移動,則情況二進行了\(m - 1 -x\)次,情況三進行了\(i - x\)次移動。
則移動到\(m = 1\)的概率為
很顯然這式子整不動。通常處理方法就是交換求和順序。我們從實際意義來說明。
我們先假設進行了\(x\)次情況一的移動,則情況二進行了\(m - 1 -x\)次,情況三進行了\(i\)次移動,其中\(0 \leq i \leq n - 1 - x\)。
則移動到\(m=1\)的概率為
而后面這一項可以事先預處理一個前綴和\(S(r) = \sum\limits_{i = 0}^{r} C_{m - 1 + i}^{i}c^{i}\)
這樣,最終的答案就是
值得注意的是在計算\(b^{m - 1 - x}\)時,萬萬不可算出\(b^{m-1}\)然后除以\(b\)除以\(b\)(取模意義上)。
因為當\(b=0\)的時候,這樣算的話\(0^{0} = 0\)。
而實際上我們應該認為\(0^{0} = 1\),也就是說應當采用快速冪計算 (雖然會多個log)。
神奇的代碼
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
template <typename T>
void read(T &x)
{
int s = 0, c = getchar();
x = 0;
while (isspace(c))
c = getchar();
if (c == 45)
s = 1, c = getchar();
while (isdigit(c))
x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
if (s)
x = -x;
}
template <typename T>
void write(T x, char c = ' ')
{
int b[40], l = 0;
if (x < 0)
putchar(45), x = -x;
while (x > 0)
b[l++] = x % 10, x /= 10;
if (!l)
putchar(48);
while (l)
putchar(b[--l] | 48);
putchar(c);
}
const LL mo = 998244353;
const int N = 2e5 + 8;
int n, m;
LL jie[N], invjie[N];
LL sum[N];
LL p, q;
LL a, b, c, d;
LL inv100 = 828542813;
LL ans;
LL qpower(LL a, LL b)
{
LL qwq = 1;
while (b)
{
if (b & 1)
qwq = qwq * a % mo;
b >>= 1;
a = a * a % mo;
}
return qwq;
}
LL inv(LL x)
{
return qpower(x, mo - 2);
}
LL C(int n, int m)
{
if (n < m)
return 0;
return jie[n] * invjie[m] % mo * invjie[n - m] % mo;
}
int main(void)
{
int kase;
read(kase);
jie[0] = invjie[0] = 1;
for (int i = 1; i < N; ++i)
{
jie[i] = jie[i - 1] * i % mo;
invjie[i] = inv(jie[i]);
}
for (int ii = 1; ii <= kase; ii++)
{
read(n);
read(m);
read(p);
read(q);
p = p * inv100 % mo;
q = q * inv100 % mo;
d = inv((1 - (1 - p) * (1 - q) % mo + mo) % mo);
a = p * q % mo * d % mo;
b = p * ((1 - q + mo) % mo) % mo * d % mo;
c = q * ((1 - p + mo) % mo) % mo * d % mo;
sum[0] = 1;
LL tmp = c;
for (int i = 1; i < n; ++i)
{
sum[i] = (sum[i - 1] + tmp * C(m - 1 + i, i) % mo) % mo;
tmp = tmp * c % mo;
}
ans = 0;
LL qaq = 1;
// LL qbq = qpower(b, m - 1);
// LL invb = inv(b);
int up = min(m - 1, n - 1);
for (int i = 0; i <= up; ++i)
{
ans = (ans + qaq * qpower(b, m - i - 1) % mo * C(m - 1, i) % mo * sum[n - i - 1] % mo) % mo;
// ans = (ans + qaq * qbq % mo * C(m - 1, i) % mo * sum[n - i - 1] % mo) % mo; // 0^0 = 1
qaq = qaq * a % mo;
// qbq = qbq * invb % mo;
}
ans = ans * p % mo * d % mo;
write(ans, '\n');
}
return 0;
}
F. Query on the Tree (Hdu 6843)
題目大意
qwq
解題思路
qwq
神奇的代碼
qwq