题目链接:https://codeforces.com/gym/100502
D - Dice Game
签到题1,和上次写的思路一样,先枚举每个人的两个骰子的结果,算出每个值的概率,然后再暴力枚举两次值,算出每个值的获胜概率和失败概率。每个事件的概率都是相等的,所以可以直接用整数。
int p1[205];
int p2[205];
void test_case() {
int l1, r1, l2, r2;
scanf("%d%d%d%d", &l1, &r1, &l2, &r2);
int sum1 = 0;
for(int i = l1; i <= r1; ++i) {
for(int j = l2; j <= r2; ++j) {
++p1[i + j];
++sum1;
}
}
scanf("%d%d%d%d", &l1, &r1, &l2, &r2);
int sum2 = 0;
for(int i = l1; i <= r1; ++i) {
for(int j = l2; j <= r2; ++j) {
++p2[i + j];
++sum2;
}
}
ll W = 0, T = 0, L = 0;
for(int i = 1; i <= 200; ++i) {
for(int j = 1; j < i; ++j)
W += p1[i] * p2[j];
T += p1[i] * p2[i];
for(int j = i + 1; j <= 200; ++j)
L += p1[i] * p2[j];
}
if(W > L)
puts("Gunnar");
else if(W < L)
puts("Emma");
else
puts("Tie");
}
K - Train Passengers
签到题2,直接模拟。
void test_case() {
int C, n;
scanf("%d%d", &C, &n);
ll cur = 0;
for(int i = 1; i <= n; ++i) {
int out, in, wait;
scanf("%d%d%d", &out, &in, &wait);
if(out > cur) {
puts("impossible");
return;
}
cur -= out;
cur += in;
if(cur > C) {
puts("impossible");
return;
}
if(cur < C && wait != 0) {
puts("impossible");
return;
}
}
if(cur != 0) {
puts("impossible");
return;
}
puts("possible");
return;
}
E - Opening Ceremony
签到题3。
题意:给一串数字,表示一些大楼的高度,每次操作:1:把一栋大楼清空,或者操作2:把每栋大楼(假设还有)都减少1。问最少的操作次数。
题解:贪心,排序,然后枚举中间点,中间点及其之前的全部用操作2清空,后面的全部用操作1清空。
int a[100005];
void test_case() {
int n;
scanf("%d", &n);
for(int i = 1; i <= n; ++i)
scanf("%d", &a[i]);
sort(a + 1, a + 1 + n);
int ans = n;
for(int i = 1; i <= n; ++i)
ans = min(ans, a[i] + n - i);
printf("%d\n", ans);
}
H - Clock Pictures
题意:有两个钟,都是有n根(2<=n<=200000)互不重叠的指针,他们分别位于一些刻度,刻度之间无法区分,所以不妨从0点开始标记刻度,刻度的范围是[0,360000)。问这两个钟是否有可能指示同一个时刻。
题解:也就是说指针之间的角度是钟的唯一特征,作一个差分,然后把第二个钟的差分序列延长一倍,问题变成在第二个钟的差分序列上是否存在第一个钟的差分序列的一个完全匹配。当时没想到用KMP做,用的多项式hash,然后用一发WA证明了多项式哈希假如BASE不取质数的话确实会WA。
看学弟居然还能随机选一些点验证的,比我还暴力,遇到出锅场就很强。
int a[200005];
int b[400005];
int d[400005];
void test_case() {
int n;
scanf("%d", &n);
for(int i = 1; i <= n; ++i)
scanf("%d", &a[i]);
for(int i = 1; i <= n; ++i)
scanf("%d", &b[i]);
sort(a + 1, a + 1 + n);
sort(b + 1, b + 1 + n);
for(int i = 1; i <= n; ++i)
b[i + n] = b[i];
for(int i = 1; i <= 2 * n; ++i) {
d[i] = b[i] - b[i - 1];
if(d[i] < 0)
d[i] += 360000;
}
ll BASE = 233;
ll ha = 0;
for(int i = 2; i <= n; ++i) {
ha = ha * BASE + (a[i] - a[i - 1]);
if(ha >= MOD)
ha %= MOD;
}
ll ha2 = 0;
for(int i = 2; i <= n; ++i) {
ha2 = ha2 * BASE + d[i];
if(ha2 >= MOD)
ha2 %= MOD;
}
if(ha == ha2) {
puts("possible");
return;
}
ll BASEK = qpow(BASE, n - 1);
for(int i = n + 1; i <= 2 * n; ++i) {
ha2 = ha2 * BASE + d[i];
if(ha2 >= MOD)
ha2 %= MOD;
ha2 = ha2 - d[i - n + 1] * BASEK % MOD;
if(ha2 < 0)
ha2 += MOD;
if(ha == ha2) {
puts("possible");
return;
}
}
puts("impossible");
return;
}
收获:
1、多项式哈希,确定要去掉的那一项被乘了多少次BASE,把这个项乘上同样的次数的BASE然后减掉,就可以从区间中去掉前面的项,同样的,假如存好每个位置的前缀哈希值,那么可以用后面的哈希减去前面的哈希乘上长度(多的BASE),就得到中间项的哈希。
2、可以取一系列为1的字符,然后混一个为2的字符,BASE取10,这样可以很容易看见哈希的过程。
写一下KMP和随机法搞过去的吧。