首页 > 其他分享 >2021 牛客 OI 赛前集训营-提高组(第二场)

2021 牛客 OI 赛前集训营-提高组(第二场)

时间:2023-03-12 17:13:55浏览次数:37  
标签:OI int text d% 个数 牛客 maxn 集训营 id

A.串串串

【题目描述】

你有两个长度分别为 \(n, m\) 的 \(01\) 串 \(\text{S, T}\)。

有 \(\text Q\) 次询问,每次询问给出 \(l_1, r_1, l_2, r_2\),其中 \(r_1 − l_1 + 1 = r_2 − l_2 + 1\)。

令 \(a = \text S[l_1 … r_1], b = \text T[l_2 … r_2]\)。

你需要求出 \(a_i ≠ b_i\) 的位置个数对 \(2\) 取模的结果

【解决方法】

一、暴力

暴力枚举 \(i\),计算 \(a_i\not= b_i\) 的个数之和对 \(2\) 取模的结果。
时间复杂度:\(\text O(q\cdot\max(n,m))\)

#include <bits/stdc++.h>
using namespace std;

const int maxn = 1e6 + 7;
int n, m, q, cnt;
char s1[maxn], s2[maxn];

int main()
{
	scanf("%d%d%s%s",&n,&m,s1+1,s2+1);
	scanf("%d",&q);
	for (int i = 1, l1,l2,r1,r2; i <= q; ++ i)
	{
		cnt = 0;
		scanf("%d%d%d%d",&l1,&r1,&l2,&r2);
		for (int i = l1, j = l2; j <= r2; ++ j, ++ i)
		{
			if (s1[i] != s2[i]) cnt ++; 
		}
		printf("%d\n",cnt%2);
	}
	return 0;
}

二、性质

我们发现对于 \(\begin{matrix}1&\dots&0\ \ (\text S)\\0&\dots&0\ \ (\text T)\end{matrix}\),如果交换 \(\text S\) 的 \(1\) 与 \(0\) 的位置,不同的数对还是 \(1\)。类似地,

  1. 若相同的两对,且上下两个数均相同的交换后无影响;

  2. 若是不同的两对,且上下两个数均相同的交换后,贡献由 \(0\) 变成了 \(2\),模 \(2\) 后还是无影响;

  3. 若相同的两对,且上下两个数不相同的交换后无影响;

  4. 若是不同的两对,且上下两个数均相同的交换后,贡贡献由 \(2\) 变成了 \(0\),模 \(2\) 后还是无影响;

因此,若将这范围的 \(1\) 放到前面的话,那么不同的数的个数就是 \(a\) 中 \(1\) 的个数和 \(b\) 中 \(1\)的个数之差。

#include <bits/stdc++.h>
using namespace std;

const int maxn = 1e6 + 7;
int n, m, q, b1[maxn], b2[maxn];
char s1[maxn], s2[maxn];

int main()
{
	scanf("%d%d%s%s",&n,&m,s1+1,s2+1);
	for (int i = 1; i <= n; ++ i)
	{
		b1[i] = b1[i-1] + (s1[i]=='1');
	}
	for (int i = 1; i <= m; ++ i)
	{
		b2[i] = b2[i-1] + (s2[i]=='1');
	}
	scanf("%d",&q);
	for (int i = 1, l1,l2,r1,r2; i <= q; ++ i)
	{
		scanf("%d%d%d%d",&l1,&r1,&l2,&r2);
		printf("%d\n",(abs(b1[r1]-b1[l1-1]-b2[r2]+b2[l2-1]))%2);
	}
	return 0;
}

三、异或

当然,由于是 \(01\) 串,并且昨天刚刚看了看 \(\text{bitset}\),然后就通过它的左移右移操作取出 \(\text S[l_1\dots r_1]\) 和 \(\text T[l_2\dots r_2]\) 的范围的数,然后异或,得到不同的数的个数,返回 \(1\) 的个数模 \(2\) 即可。

结果,炸的和暴力一个分。

如果,再想想的话,异或后 \(1\) 的个数也就可以把最后的 \(\text{bitset}\) 里面的数得到一个异或和。

那,还用个锤子的 \(\text{bitset}\) 啊,异或前缀和解决。

#include <bits/stdc++.h>
using namespace std;

int n, m, q, Xor[2][2000100];
char s[2][2000100];

int main()
{
    scanf("%d%d%s%s",&n,&m,s[0]+1,s[1]+1);
    for (int i = 1; i <= n; ++ i) Xor[0][i] = Xor[0][i-1]^(s[0][i]-'0');
    for (int i = 1; i <= m; ++ i) Xor[1][i] = Xor[1][i-1]^(s[1][i]-'0');
    scanf("%d",&q);
    for (int i = 1, l1, r1, l2, r2; i <= q; ++ i)
    {
        scanf("%d%d%d%d",&l1,&r1,&l2,&r2);
        printf("%d\n",Xor[0][r1]^Xor[0][l1-1]^Xor[1][r2]^Xor[1][l2-1]);
    }
    return 0;
}

对于 \(100\%\) 的数据,\(1 ≤ n, m, q ≤ 2 × 10^5,1 ≤ l_1 ≤ r_1 ≤ n,1 ≤ l_2 ≤ r_2 ≤ m\)。


B.方格计数

【题目描述】

在左下角是 \((0, 0)\),右上角是 \((W,H)\) 的网格上,有 \((W + 1) × (H + 1)\) 个格点。

现在要在格点上找 \(N\) 个不同的点,使得这些点在一条直线上。

并且在这条直线上,相邻点之间的距离不小于 \(D\)。求方案数模 \(10^9 + 7\)。

【解决方法】

【算法一】

考虑枚举两个端点,强制两个端点选,令 \(a\) 为两个端点之间 \(x\) 轴上的距离,\(b\) 为两个端点 \(y\) 轴上的距离,那么这里面可以选择的点的个数有 \(g = \gcd(a, b)\) 个。我们要求 \(N − 2\) 个小球(强制两个端点选),需要放到 \(g\) 个盒子里,相邻两个小球的盒子编号差至少为 \(k\),方案数为 \(C^{N − 2}_{g + 1 − 2k − (N − 3)(k − 1))}\)。时间复杂度 \(\text O(TW^2H^2)\),

【算法二】

延续算法一的思路,发现对于相同的 \(a, b\) 方案数也是相同的,考虑枚举 \(a, b\),跟算法一一样做,最后再乘个 (W − a + 1) × (H − b + 1) 就好了,时间复杂度 \(\text O(TWH)\)。

对于 \(100\%\) 的数据,\(1 ≤ N ≤ 50,1 ≤ W,H,D ≤ 500,1 ≤ T ≤ 20\)。

C.树数树

【题目描述】

牛牛有一棵 \(n\) 个点的有根树,根为 \(1\)。

我们称一个长度为 \(m\) 的序列 \(a\) 是好的,当且仅当:

相关文章