Codeforces 524 解题报告
打的很快乐的一次比赛hiahiahia, 才A掉4题rating就涨了100+
距离比赛\(3\)天了, 由于博主实在太颓, 又补掉了\(E\)题, 到现在才发解题报告
A.
语法题, 读入输出就行了
#include<cstdio>
#include<algorithm>
#include<iostream>
#define rd read()
#define ll long long
using namespace std;
int read() {
int X = 0, p = 1; char c = getchar();
for (; c > '9' || c < '0'; c = getchar())
if (c == '-') p = -1;
for (; c <= '9' && c >= '0'; c = getchar())
X = X * 10 + c - '0';
return X * p;
}
int main()
{
int n = rd, k = rd;
ll a = 2 * n, b = 5 * n, c = 8 * n, ans = 0;
ans = (a + k - 1) / k + (b + k - 1) / k + (c + k - 1) / k;
cout << ans << endl;
}
B.
把相邻两项放在一起, 多出的\(1\)项另外加上去
#include<cstdio>
#include<algorithm>
#define rd read()
using namespace std;
int read() {
int X = 0, p = 1; char c = getchar();
for (; c > '9' || c < '0'; c = getchar())
if (c == '-') p = -1;
for (; c <= '9' && c >= '0'; c = getchar())
X = X * 10 + c - '0';
return X * p;
}
int main()
{
int n = rd;
for (; n; --n) {
int l = rd, r = rd, ans = 0;
if ((r - l + 1) % 2 == 0) printf("%d\n", (l & 1 ? 1 : -1) * (r - l + 1) / 2);
else {
ans = (l & 1 ? 1 : -1) * (r - l) / 2;
ans += (r & 1 ? -1 : 1) * r;
printf("%d\n", ans);
}
}
}
C.
简单的容斥
白色和黑色是交替出现的
一个\(w \times h\) 的矩阵, 如果 \(w \times h\) 为奇数, 则左下角的颜色会比另外一种颜色多出\(1\)个
接下来是计算方法, 算出的 黑色 或 白色 的个数表示 泼墨水之前的矩阵
泼白墨水时, 算出 \((x1, y1), (x2, y2)\) 这个矩阵内有多少个黑色方块, 并更新入答案
泼黑墨水时, 算出 \((x3, y3), (x4, y4)\) 这个矩阵内有多少个白色方块, 并更新入答案
这时我们发现两次泼墨水的范围会有交集, 这个相交的矩阵为 \((x5, y5), (x6, y6)\)
这个相交的矩阵内 第二次泼墨只记入了 白色方块的贡献, 而没有记入黑色方块的贡献(黑色方块在第一次泼墨时被染成了白色)
所以只需要把 这个矩阵内的 黑色方块的个数 加入黑色方块的总数即可
枚举横纵坐标就可以找到相交矩阵
下代码计算矩阵内某种颜色的方块数用一个函数可以解决, 我打麻烦了QAQ
#include<cstdio>
#include<algorithm>
#include<iostream>
#define rd read()
#define ll long long
using namespace std;
typedef pair<ll, ll> P;
ll n, m, numw, numb;
ll lsx[10], lsy[10], cntx, cnty;
const ll inf = 1e9 + 7;
ll read() {
ll X = 0, p = 1; char c = getchar();
for (; c > '9' || c < '0'; c = getchar())
if (c == '-') p = -1;
for (; c <= '9' && c >= '0'; c = getchar())
X = X * 10 + c - '0';
return X * p;
}
#define X first
#define Y second
void in(P &tmp) {
tmp.X = rd; tmp.Y = rd;
lsx[++cntx] = tmp.X;
lsy[++cnty] = tmp.Y;
}
int jud(ll x, ll y, P tmp1, P tmp2, P tmp3, P tmp4) {
if (x < tmp1.X || x > tmp2.X) return 0;
if (x < tmp3.X || x > tmp4.X) return 0;
if (y < tmp1.Y || y > tmp2.Y) return 0;
if (y < tmp3.Y || y > tmp4.Y) return 0;
return 1;
}
void up(P &a, P b) {
if (a < b) a = b;
}
void down(P &a, P b) {
if (a > b) a = b;
}
void cal() {
P tmp1, tmp2, tmp3, tmp4;
in(tmp1); in(tmp2); in(tmp3); in(tmp4);
ll dx = tmp2.X - tmp1.X + 1, dy = tmp2.Y - tmp1.Y + 1;
if ((dx * dy) % 2 && (tmp1.X + tmp1.Y) % 2) {
numb -= dx * dy - dx * dy / 2;
numw += dx * dy - dx * dy / 2;
} else numb -= dx * dy / 2,
numw += dx * dy / 2;
dx = tmp4.X - tmp3.X + 1, dy = tmp4.Y - tmp3.Y + 1;
if ((dx * dy) % 2 && (tmp3.X + tmp3.Y) % 2 == 0) {
numw -= dx * dy - dx * dy / 2;
numb += dx * dy - dx * dy / 2;
} else numw -= dx * dy / 2,
numb += dx * dy / 2;
P tmp5 = P(inf, inf), tmp6 = P(0, 0);
for (int i = 1; i <= 4; ++i)
for (int j = 1; j <= 4; ++j) if (jud(lsx[i], lsy[j], tmp1, tmp2, tmp3, tmp4)) {
down(tmp5, P(lsx[i], lsy[j])),
up(tmp6, P(lsx[i], lsy[j]));
}
if (tmp6.X == 0) return;
dx = tmp6.X - tmp5.X + 1, dy = tmp6.Y - tmp5.Y + 1;
if ((dx * dy) % 2 && (tmp5.X + tmp5.Y) % 2) {
numw -= dx * dy - dx * dy / 2;
numb += dx * dy - dx * dy / 2;
} else numw -= dx * dy / 2,
numb += dx * dy / 2;
}
#undef x
#undef y
int main()
{
int T = rd;
for (; T; T--) {
cntx = cnty = 0;
n = rd; m = rd;// y <= n && x <= m (x, y)
numb = n * m / 2;
numw = n * m - numb;
cal();
cout << numw << " " << numb <<endl;
}
}
D.
枚举+数列计算?
\(a_n=(4^n-1) \div 3\) 表示边长为 \(2^n\) 的矩阵分裂成 \(1 \times 1\) 的矩阵需要的操作数。 高中的数列知识应该算的很快
然后枚举 路径上的矩阵的边长为 \(2^{n-a} \ (a=1...n)\) , 至少要 \(2^a - 1\) 次操作才能造出存在这样的路径,
则左边界\(L=2^a-1\), 然后我们需要算出右边界\(R\),并判断\(k\) 是否在 \([L, R]\) 内。
把除掉这条路径上的 所有矩阵都分裂成 \(1 \times 1\) 的矩阵 就能算出 \(R\)
另外 \(R\) 的值会爆\(LL\), 所以我用了 \(long \ double\) QuQ
#include<cstdio>
#include<algorithm>
#include<iostream>
#define ll long long
#define rd read()
#define lb long double
using namespace std;
const ll inf = 1e9 + 7;
ll n, k;
ll read() {
ll X = 0, p = 1; char c = getchar();
for (; c > '9' || c < '0'; c = getchar())
if (c == '-') p = -1;
for (; c >= '0' && c <= '9'; c = getchar())
X = X * 10 + c - '0';
return X * p;
}
lb fpow(lb a, ll b) {
lb res = 1;
for (; b; b >>= 1, a = a * a)
if (b & 1) res = res * a;
return res;
}
int cal() {
lb tmp1 = 0, tmp2 = 0, fac = 2;
for (int i = 1; i <= n && i <= 50; ++i) {
tmp1 += fac - 1;
tmp2 += (2 * (fac - 1) - 1) * (fpow(4, n - i) - 1) / 3;
if (tmp1 <= k && k <= tmp1 + tmp2) return n - i;
if (tmp1 > k) return inf;
fac *= 2;
}
return inf;
}
int main()
{
int T = rd;
for (; T; T--) {
n = rd; k = rd;
int res = cal();
if (res == inf) puts("NO");
else printf("YES %d\n", res);
}
}
E.
manacher算法
一个矩阵满足条件, 必须使
-
- 在每一行中, 出现次数为奇数的字符数 \(<=1\)
- 对应两行每种字符的个数相同
然后枚举列,把每一行看成一个字符, 进行manacher算出回文串的个数即可
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<bitset>
#define ll long long
using namespace std;
typedef bitset<28> BT;
const int N = 260;
int n, m, cnt[N * 2][N][27], len[N << 1], odd[N << 1];
BT num[N << 1][N];
char s[N][N];
int cmp(int L, int R, int x, int y) {
if (odd[x] > 1 || odd[y] > 1) return 0;
for (int k = 1; k <= 26; ++k)
if (cnt[x][R][k] - cnt[x][L - 1][k] != cnt[y][R][k] - cnt[y][L - 1][k]) return 0;
return 1;
}
int cal(int l, int r) {
len[1] = 1; int pos = 1, R = 1;
int res = 0;
for (int i = 2; i <= 2 * n + 1; ++i) {
if (i <= R) {
if (len[2 * pos - i] < R - i + 1) len[i] = len[2 * pos - i];
else {
len[i] = R - i + 1;
while (i + len[i] <= 2 * n + 1 && i - len[i] && cmp(l, r, i + len[i], i - len[i]))
R++, len[i]++, pos = i;
}
} else {
len[i] = 1;
while (i + len[i] <= 2 * n + 1 && i - len[i] && cmp(l, r, i + len[i], i - len[i]))
R++, len[i]++, pos = i;
}
if (odd[i] <= 1) res += len[i] / 2;
}
// printf("%d\n", res);
return res;
}
int main()
{
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; ++i)
scanf("%s", s[i] + 1);
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= m; ++j) {
for (int k = 1; k <= 26; ++k)
cnt[i << 1][j][k] = cnt[i << 1][j - 1][k];
cnt[i << 1][j][s[i][j] - 'a' + 1]++;
BT tmp; tmp.set(s[i][j] - 'a' + 1);
num[i << 1][j] = num[i << 1][j - 1] ^ tmp;
}
int ans = 0;
for (int i = 1; i <= m; ++i)
for (int j = 1; j <= i; ++j) {
BT tmp;
for (int k = 1; k <= n; ++k) {
tmp = num[k << 1][j - 1] ^ num[k << 1][i];
odd[k << 1] = tmp.count();
}
// printf("%d %d :", j, i);
ans += cal(j, i);
}
printf("%d\n", ans);
}