pyyz集训Day3

image
依旧差评如潮


这是今天的题面: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

求出全源最短路。
然后注意到新一轮:

\[p_i' = \sum_{j\not= i}\left(\dfrac{d_{j,i}}{sumd_j}\cdot p_j\right) \]

这个东西可以用矩阵快速幂。
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
可以看原题的题解,我这有一种更麻烦的做法。
考虑到直径最小的时候就是长这样:
image
此时的直径是 \(n - 1 + \lceil \frac{m - 1}{2}\rceil\times 2\) ,如果不够 \(k\),我们就一点点微调。
具体怎么调呢?
我们把这一整条竖线看成一堆长度为 \(1\) 的小竖线。
我们首先将最上面的小竖线往左移一个单位,此时的直径会+2,一直移到最左边(如果直径还小于 \(k\)),然后把下一行的小竖线慢慢往右移,一直这么重复。
如果某个时候直径 \(k - 1\) 了,说明下一次我们调整就会多出 \(1\),我们考虑怎么补这个 \(1\)
假设最后两行长这样:
image
我们直接调整成这样:
image
此时直径恰好增加 \(1\)
但是如果我们最后一次跳是跳的最后一个小竖线,那我们如果这么调整显然会破坏结构,我们得换一种调整方法。
image
调整成这样即可。
再就是一些细节问题了。
代码我调了 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

posted @ 2026-08-09 20:40  SigmaToT  阅读(8)  评论(0)    收藏  举报