QBXT2025S刷题 Day2

今天的题目

\(rk38\)

T1

这道题想了 \(1h\) 差不多。
这道题其实可以直接转化成选一个点,把覆盖着这个点线段全部删掉,使得左右两边都有线段。
可以维护每个点被多少个区间覆盖,左面有多少个区间,右面有多少个区间。
这个可以差分。
注意,\([1,6]\)\([7, 8]\) 之间是没有点的,但是他们之间的那一段也得考虑。
因此我们可以把所有区间都乘以 \(2\),就相当于在他们中间插入一个点。
这是代码:

#include <iostream>
#include <vector>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <ctime>

using std::cin;
using std::cout;
const int N = 8e5 + 10;

int l[(int)2e5 + 10];
int r[(int)2e5 + 10];
int rin[N];
int lin[N];
int cnt[N];
std::vector<int> p;

void read(int &x)
{
   char c = getchar();
   x = 0;
   int f = 1;
   while (c < '0' || c > '9')
   {
   	if (c == '-')
   		f = -f;
   	c = getchar();
   }
   while (c >= '0' && c <= '9')
   {
   	x = x * 10 + c - '0';
   	c = getchar();
   }
   x = x * f;
}

int main()
{
   int t;
   read(t);
   while (t--)
   {
   	p.clear();
   	memset(lin, 0, sizeof(lin));
   	memset(rin, 0, sizeof(rin));
   	memset(cnt, 0, sizeof(cnt));
   	int n;
   	read(n);
   	for (int i = 1; i <= n; ++i)
   	{
   		read(l[i]);
   		read(r[i]);
   		p.push_back(l[i]);
   		p.push_back(r[i]);
   	}
   	std::sort(p.begin(), p.end());
   	p.erase(std::unique(p.begin(), p.end()), p.end());
   	int maxval = 0;
   	for (int i = 1; i <= n; ++i)
   	{
   		l[i] = std::lower_bound(p.begin(), p.end(), l[i]) - p.begin() + 1;
   		r[i] = std::lower_bound(p.begin(), p.end(), r[i]) - p.begin() + 1;
   		l[i] *= 2;
   		r[i] *= 2;
   		cnt[l[i]]++;
   		cnt[r[i] + 1]--;
   		maxval = std::max(maxval, r[i]);
   	}
   	for (int i = 1; i <= n; ++i)
   	{
   		rin[1]++;
   		rin[l[i]]--;
   		lin[r[i] + 1]++;
   		lin[maxval + 1]--;
   	}
   	int ans = 1e9 + 7;
   	for (int i = 1; i <= maxval; ++i)
   	{
   		cnt[i] += cnt[i - 1];
   		lin[i] += lin[i - 1];
   		rin[i] += rin[i - 1];
   		if (lin[i] && rin[i])
   			ans = std::min(ans, cnt[i]);
   	}
   	if (ans == 1e9 + 7)
   		cout << -1 << '\n';
   	else
   		cout << ans << '\n';
   }
   return 0;
}

T2

这道题有一个结论部分分,就是 \(q = 0\) 的时候。
考虑在第一列随便放,然后容易证明,后面 \(m - 1\) 列每一列要么和前一列一样,要么是前一列取反。
所以答案是 \(2^n * 2^{m - 1}\)
然后在对第一个部分分暴力就行。

正解:

结论:\(a_{i,j} = a_{i - 1, j} \oplus a_{i, j - 1} \oplus a_{i - 1, j - 1}\)
易推出:\(a_{i,j} = a_{1, 1}\oplus a_{i, 1}\oplus a_{1, j}\)
因此,如果我们已经知道 \(a_{i,j}\)
假设 \(a_{1, 1} = 0,1\)
这个可以通过 \(\mathcal{2-SAT}\) 建边。
如果无解,就是 \(0\)
如果有解,就是 \(2^{\frac{连通块个数}{2}}\)

#include <iostream>

using std::cin;
using std::cout;
const int N = 6e5 + 10;
const int mod = 1e9 + 7;

int n, m, q;
int cnt;
int go[N << 1];
int pow2[N << 1];

int find(int x)
{
	if (go[x] == x)
		return x;
	return go[x] = find(go[x]);
}
void merge(int x, int y)
{
	x = find(x);
	y = find(y);
	if (x != y)
	{
		go[x] = y;
		cnt--;
	}
}
void read(int& x)
{
	x = 0;
	char c = getchar();
	int f = 1;
	while (!isdigit(c))
	{
		if (c == '-')
			f = -f;
		c = getchar();
	}
	while (isdigit(c))
	{
		x = x * 10 + c - '0';
		c = getchar();
	}
	x = x * f;
}

int main()
{
	int n, m, q;
	read(n), read(m), read(q);
	cnt = 2 * (n + m);
	pow2[0] = 1;
	for (int i = 1; i <= 2 * (n + m); ++i)
		go[i] = i;
	for (int i = 1; i <= 2 * (n + m); ++i)
		pow2[i] = 1ll * pow2[i - 1] * 2 % mod;
	cout << pow2[n + m - 1] << '\n';
	bool f = true;
	while (q--)
	{
		int x, y, c;
		read(x), read(y), read(c);
		if (!f)
		{
			cout << 0 << '\n';
			continue;
		}
		merge(x, y + n + c * (n + m));
		merge(x + n + m, y + n + (!c) * (n + m));
		if (find(x) == find(x + n + m))
		{
			cout << 0 << '\n';
			f = false;
		}
		else
			cout << pow2[cnt / 2 - 1] << '\n';
	}
	return 0;
}

T3

\(3\) 个部分分可以输出字符串的大小。
结论:答案是
\(\displaystyle\sum^n_{d = 1}\dfrac{\lfloor\dfrac{n}{d}\rfloor}{\displaystyle\prod_{i \in colors} \text{cnt}_i!}\)

T4

不会。
听不懂。
Pasted image 20251003213840

posted @ 2025-10-03 21:41  SigmaToT  阅读(5)  评论(0)    收藏  举报