pyyz集训Day3

依旧差评如潮
这是今天的题面:https://files.cnblogs.com/files/blogs/832279/0809-down.zip?t=1786275399&download=true
T1
我们考虑一个数 \(i\) 会被算多少次,显然是 \((i - l + 1)(r - i + 1)\)(共有多少个区间包含它)。
手玩一下,发现当 \(l, r\) 奇偶性相同时,所有数的出现顺序肯定是 奇、偶、奇、偶、奇...奇 ,也就是只需要维护隔一个数算一个的前缀异或和,也就是需要维护所有下标为奇数的前缀异或和、所有下标为偶数的前缀异或和。
当 \(l, r\) 奇偶性不同时,发现所有数出现的次数都是偶数,所以答案是 \(0\)。
大家一定要写快读。
也可以关闭同步流(如果加了freopen就不要用,会死得很惨)。
慢速读入会被毒瘤出题人卡掉45pts。
点击查看代码
#include <iostream>
#define lowbit(x) x & (-x)
using std::cin;
using std::cout;
const int N = 2e5 + 10;
int n, q;
int a[N];
int sm1[N];
int sm2[N];
void add1(int x, int k)
{
for (; x <= n; x += lowbit(x))
sm1[x] ^= k;
}
void add2(int x, int k)
{
for (; x <= n; x += lowbit(x))
sm2[x] ^= k;
}
int q1(int x)
{
int ret = 0;
for (; x; x -= lowbit(x))
ret ^= sm1[x];
return ret;
}
int q2(int x)
{
int ret = 0;
for (; x; x -= lowbit(x))
ret ^= sm2[x];
return ret;
}
int main()
{
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
cin >> n >> q;
for (int i = 1; i <= n; ++i)
{
cin >> a[i];
if (i & 1)
add1(i, a[i]);
else
add2(i, a[i]);
}
while (q--)
{
int opt;
cin >> opt;
int x, y;
cin >> x >> y;
if (opt == 1)
{
if (x & 1)
add1(x, a[x] ^ (a[x] = y));
else
add2(x, a[x] ^ (a[x] = y));
}
else if (opt == 2)
{
if ((x ^ y) & 1)
cout << 0 << '\n';
else
{
if (x & 1)
cout << (q1(y) ^ (q1(x - 1))) << '\n';
else
cout << (q2(y) ^ (q2(x - 1))) << '\n';
}
}
}
return 0;
}
T2
我们注意到数据范围是 \(n\le 20\)。
因此我们可以在 \(\mathcal O(n2^n)\) 的时间里解决这个问题。
也就是状压dp。
我们设 \(f_{i,s}\) 表示已经填了 \(i\) 个数,所有数填的状态为 \(s\) 的最大训练效果。
就可以了。
点击查看代码
#include <iostream>
#include <vector>
#define lowbit(x) (x & (-x))
using std::cin;
using std::cout;
const int N = 25;
const int M = (1 << 20) + 10;
typedef long long ll;
const int oo = 1e9 + 10;
int a[N][N];
int aa[M];
int f[M];
int cnt[M];
int sm[N][M];
int main()
{
int n;
cin >> n;
for (int i = 1; i <= n; ++i)
{
for (int j = 1; j <= n; ++j)
cin >> a[i][j], aa[(1 << (j - 1))] = a[i][j];
for (int j = 1; j < (1 << 20); ++j)
sm[i][j] = sm[i][j ^ lowbit(j)] + aa[lowbit(j)];
}
for (int i = 0; i < (1 << 20); ++i)
{
f[i] = -oo;
if (i)
cnt[i] = cnt[i ^ lowbit(i)] + 1;
}
f[0] = 0;
for (int i = 1; i <= n; ++i)
{
for (int j = (1 << 20) - 1; j >= 0; --j)
{
if (cnt[j] != i - 1)
continue;
for (int k = 1; k <= n; ++k)
{
if ((j >> (k - 1)) & 1)
continue;
f[j | (1 << (k - 1))] = std::max(f[j | (1 << (k - 1))], f[j] + sm[k][j]);
}
}
}
cout << f[(1 << n) - 1] << '\n';
return 0;
}
T3
求出全源最短路。
然后注意到新一轮:
这个东西可以用矩阵快速幂。
floyd的三重循环一定要像我这么写!!!否则会WA!!!
矩阵乘法的三重循环一定要像我这么写!!!否则会TLE!!!
点击查看代码
#include <iostream>
#include <cstring>
#include <cstdio>
using std::cin;
using std::cout;
typedef double db;
const int N = 210;
struct Mat
{
int n, m;
db a[N][N];
Mat()
{
n = m = 0;
memset(a, 0, sizeof(a));
}
void init(int x)
{
n = m = x;
memset(a, 0, sizeof(a));
for (int i = 1; i <= x; ++i)
a[i][i] = 1;
}
friend Mat operator*(const Mat &a, const Mat &b)
{
Mat ret;
ret.n = a.n;
ret.m = b.m;
for (int i = 1; i <= a.n; ++i)
{
for (int j = 1; j <= a.m; ++j)
{
for (int k = 1; k <= b.m; ++k)
ret.a[i][k] += a.a[i][j] * b.a[j][k];
}
}
return ret;
}
} fst, k, xjh;
int sm[N];
int d[N][N];
void ksm(int t)
{
for (; t; k = k * k, t >>= 1)
{
if (t & 1)
xjh = xjh * k;
}
}
int main()
{
int n, t;
cin >> n >> t;
fst.n = n, fst.m = 1;
xjh.init(n);
for (int i = 1; i <= n; ++i)
cin >> fst.a[i][1];
for (int i = 1; i <= n; ++i)
{
for (int j = 1; j <= n; ++j)
cin >> d[i][j];
}
for (int i = 1; i <= n; ++i)
{
for (int j = 1; j <= n; ++j)
{
for (int k = 1; k <= n; ++k)
d[j][k] = std::min(d[j][k], d[j][i] + d[i][k]);
}
}
k.n = k.m = n;
for (int i = 1; i <= n; ++i)
{
for (int j = 1; j <= n; ++j)
sm[i] += d[i][j];
}
for (int i = 1; i <= n; ++i)
{
for (int j = 1; j <= n; ++j)
k.a[i][j] = (db)d[j][i] / sm[j];
}
ksm(t);
fst = xjh * fst;
for (int i = 1; i <= n; ++i)
printf("%.6lf\n", fst.a[i][1]);
return 0;
}
T4
原题:https://www.luogu.com.cn/problem/P6663
可以看原题的题解,我这有一种更麻烦的做法。
考虑到直径最小的时候就是长这样:

此时的直径是 \(n - 1 + \lceil \frac{m - 1}{2}\rceil\times 2\) ,如果不够 \(k\),我们就一点点微调。
具体怎么调呢?
我们把这一整条竖线看成一堆长度为 \(1\) 的小竖线。
我们首先将最上面的小竖线往左移一个单位,此时的直径会+2,一直移到最左边(如果直径还小于 \(k\)),然后把下一行的小竖线慢慢往右移,一直这么重复。
如果某个时候直径 \(k - 1\) 了,说明下一次我们调整就会多出 \(1\),我们考虑怎么补这个 \(1\)。
假设最后两行长这样:

我们直接调整成这样:

此时直径恰好增加 \(1\)。
但是如果我们最后一次跳是跳的最后一个小竖线,那我们如果这么调整显然会破坏结构,我们得换一种调整方法。

调整成这样即可。
再就是一些细节问题了。
代码我调了 6h。
点击查看代码
#include <iostream>
using std::cin;
using std::cout;
bool vis[1010];
void put(int a, int b, int c, int d, int f)
{
if (!f)
cout << a << ' ' << b << ' ' << c << ' ' << d << '\n';
else
cout << b << ' ' << a << ' ' << d << ' ' << c << '\n';
}
int main()
{
int n, m, k;
cin >> n >> m >> k;
if (std::min(n - 1 + m / 2 * 2, m - 1 + n / 2 * 2) > k || k > n * m - 1)
{
cout << "NIE" << '\n';
return 0;
}
cout << "TAK" << '\n';
if (n == 1)
{
for (int j = 1; j < m; ++j)
cout << 1 << ' ' << j << ' ' << 1 << ' ' << j + 1 << '\n';
return 0;
}
if (m == 1)
{
for (int j = 1; j < n; ++j)
cout << j << ' ' << 1 << ' ' << j + 1 << ' ' << 1 << '\n';
return 0;
}
bool f = false;
if (n - 1 + m / 2 * 2 > m - 1 + n / 2 * 2)
std::swap(n, m), f = true;
int nk = n - 1 + m / 2 * 2;
int i = 0;
int j = (m + 1) / 2;
int fl = 0;
for (i = 1; i < n; ++i)
{
if (i & 1)
{
for (j = (m + 1) / 2; j > 1; --j, nk += 2)
{
if (nk == k - 1)
{
fl = 1;
break;
}
if (nk == k)
{
fl = 2;
break;
}
}
if (fl)
break;
}
else
{
for (j = (m + 1) / 2; j < m; ++j, nk += 2)
{
if (nk == k - 1)
{
fl = 1;
break;
}
if (nk == k)
{
fl = 2;
break;
}
}
if (fl)
break;
}
}
if (fl == 1)
{
if (i == n - 1)
{
int nw = 0;
for (int i = 1; i < n - 1; ++i)
{
for (int j = 1; j < m; ++j)
{
put(i, j, i, j + 1, f);
nw++;
}
}
for (int i = 1; i < n - 1; ++i)
{
if (i & 1)
put(i, 1, i + 1, 1, f);
else
put(i, m, i + 1, m, f);
nw++;
}
if ((n - 2) & 1)
{
put(n - 1, 1, n, 1, f);
vis[1] = true;
nw++;
int p = 1;
while (p < m && nw < k)
{
put(n, p, n, p + 1, f);
nw++;
p++;
}
put(n, m, n - 1, m, f);
vis[m] = true;
nw++;
p = m;
while (p > 2 && nw < k)
{
put(n - 1, p, n - 1, p - 1, f);
nw++;
vis[p] = true;
vis[p - 1] = true;
p--;
}
for (int i = 1; i <= m; ++i)
{
if (!vis[i])
put(n - 1, i, n, i, f);
}
}
else
{
put(n - 1, m, n, m, f);
vis[m] = true;
nw++;
int p = m - 1;
while (p >= 1 && nw < k)
{
put(n, p, n, p + 1, f);
nw++;
p--;
}
put(n, 1, n - 1, 1, f);
nw++;
vis[1] = true;
p = 2;
while (p < m && nw < k)
{
put(n - 1, p, n - 1, p - 1, f);
nw++;
vis[p] = true;
vis[p - 1] = true;
p++;
}
for (int i = 1; i <= m; ++i)
{
if (!vis[i])
put(n - 1, i, n, i, f);
}
}
return 0;
}
for (int i = 1; i <= n; ++i)
{
for (int j = 1; j < m; ++j)
{
if (((n - 2) & 1) && j == 1 && i == n - 1)
continue;
if (!((n - 2) & 1) && j == m - 1 && i == n - 1)
continue;
put(i, j, i, j + 1, f);
}
}
for (int s = 1; s < i; ++s)
{
if (s & 1)
put(s, 1, s + 1, 1, f);
else
put(s, m, s + 1, m, f);
}
put(i, j, i + 1, j, f);
if ((n - 2) & 1)
put(n - 1, 1, n, 1, f);
else
put(n - 1, m, n, m, f);
for (int s = i + 1; s < n; ++s)
put(s, (m + 1) / 2, s + 1, (m + 1) / 2, f);
}
else if (fl == 2)
{
for (int i = 1; i <= n; ++i)
{
for (int j = 1; j < m; ++j)
put(i, j, i, j + 1, f);
}
for (int s = 1; s < i; ++s)
{
if (s & 1)
put(s, 1, s + 1, 1, f);
else
put(s, m, s + 1, m, f);
}
put(i, j, i + 1, j, f);
for (int s = i + 1; s < n; ++s)
put(s, (m + 1) / 2, s + 1, (m + 1) / 2, f);
}
else
{
for (int i = 1; i <= n; ++i)
{
for (int j = 1; j < m; ++j)
put(i, j, i, j + 1, f);
}
for (int s = 1; s < n; ++s)
{
if (s & 1)
put(s, 1, s + 1, 1, f);
else
put(s, m, s + 1, m, f);
}
}
return 0;
}
今天讲的题:https://vjudge.net/problem/AtCoder-abc464_f
https://vjudge.net/problem/AtCoder-abc464_g

浙公网安备 33010602011771号