CSP 日照集训考试 Day3

考的还不错,但是没干过内裤。

T1

好一眼啊这个题。

化个式子就做出来了。

\[\frac{a}{x}+\frac{b}{c} = \frac{d}{y}\\ \frac{ac+bx}{xc}=\frac{d}{y}\\ cdx=acy+bxy\\ (cd-by)x=acy\\ x=\frac{acy}{cd-by} \]

然后要求的是若干个正整数解,所以 \(x,y > 0\)

因为 \(a,b,c,d\) 都是正整数。

所以 \(acy\) 一定是一个正数,那想要让 \(x>0\) ,那么 \(dc-by>0\)

\(y<\frac{cd}{b}\)

然后就枚举 \(y\)\(x\) 即可。

然后这个 \(cd <= 1e6\) ,所以复杂度很正确。

# include <bits/stdc++.h>
using namespace std;
#define int long long
inline int read () {
	int x = 0, f = 1;
	char c = getchar ();
	while (c < '0' || c > '9') {if (c == '-') f = -1; c = getchar ();}
	while (c >= '0' && c <= '9') {x = x * 10 + c - '0', c = getchar ();}
	return x * f;
}
int a, b, c, d;
signed main () {
	int t; cin >> t;
	while (t --) {
		a = read (), b = read (), c = read (), d = read ();
		int ans = 0;
		for (int y = 1; y <= c*d/b + 10; ++ y) {
			if (c*d-b*y <= 0) break;
			int x = a*c*y/(c*d-b*y);
			if (x*(c*d-b*y) != a*c*y) continue;
			if (x>0&&y>0) ans++;
		}
		cout << ans << "\n";
	}
}

T2

想了老长时间了。

貌似是先打完了 \(T3\) ,再想出这个题正解的。

我一开始以为是个线段树的题。

异或这一类题有几个很重要的东西,一个是交换律,一个就是前缀和。

那这个题就是考的前缀和。

我一开始想的思路:

sum[] 表示的是前缀异或和。

我先考虑修改某个位置比如说 \(p\) 吧,那 \(sum[p]\) 一直到 \(sum[n]\) 都会被影响到。

那我想呢,用一个线段树维护一下这个前缀异或和。

然后修改的时候从这个地方到结尾整体改一下。

接着就没思路了…想了好几种办法,都不可行,不可行指的是复杂度貌似不对。

然后去想 \(T3\) 了。

写完 \(T3\) ,回来写 \(T2\) ,我觉得实在不行了就打个暴力吧。

然后打着打着……

发现。

貌似进行一次操作对前缀和的影响只是对 \(p\) 这个位置产生影响诶。

然后切了。

具体点的思路:

因为要让某个区间异或和等于零,即 \(sum[r]\) ^ \(sum[l - 1] = 0\)

所以只要两个位置的前缀数组的值一样就 ok 。

我们先不考虑别的,先想这个原数组的答案怎么算。

我一开始想了不少方法,有组合数之类的。但是貌似不太行,因为要预处理阶乘,而且这个预处理到什么程度也不知道。

所以,我们考虑一个数对其他数的贡献。一个区间需要两个位置就能确定,所以一个位置对其他位置的贡献就是跟这个位置 \(num\) 值相等的位置的个数,然后会发现,会重复计算贡献,所以出来答案的时候除以 \(2\)

考虑删掉和加上某个数产生的贡献。

想要删掉这个位置的数,那他与其他位置构成的区间的个数就要减掉;因为一开始记录答案的时候是算的重复的,所以其他位置与这个位置构成的区间个数也要减掉。

想要把这个位置异或上一个数,也就是添一个新的数,那他对答案的贡献就是把他的值改变后会有多少个跟他相等的数,然后其他的位置对这个位置的贡献也要重复加一下。

没啥了。代码:

# include <bits/stdc++.h>
using namespace std;
const int D = 1e6 + 100;
#define int long long
inline int read () {
	int x = 0, f = 1;
	char c = getchar ();
	while (c < '0' || c > '9') {if (c == '-') f = -1; c = getchar ();}
	while (c >= '0' && c <= '9') {x = x * 10 + c - '0', c = getchar ();}
	return x * f;
}
int n, m;
int sum[D];
map <int, int> tim;
signed main () {
	n = read (), m = read ();
	int ans = 0;
	tim[0] ++;
	sum[0] = 0;
	for (int i = 1; i <= n; ++ i) {
		sum[i] = read ();
		sum[i] = sum[i - 1] ^ sum[i];
		tim[sum[i]] ++;
	}
	for (int i = 0; i <= n; ++ i) ans += tim[sum[i]] - 1;
	int p, x;
	for (int i = 1; i <= m; ++ i) {
		p = read (), x = read ();
		ans -= 2 * (tim[sum[p]] - 1);
		tim[sum[p]] --;
		sum[p] ^= x;
		tim[sum[p]] ++;
		ans += 2 * (tim[sum[p]] - 1);
		cout << ans / 2 << "\n";
	}
}

T3

一眼线段树题。

题目中给定几个字符串,我们可以把他们看成若干条件。

例如 \(10??101\)\(1?0??01\)

需要让我们找出若干个字符串与这两个字符串匹配。

很容易就能发现,想要同时满足这两个条件,那就需要把这两个条件合并。

只要满足合并后的条件,那就一定满足这两个条件中任意的一个。

所以关键在于怎么合并条件。

可以按照以下的方式:
每一行分别表示:前一个字符串上的某一位;第二个字符串上对应的位置;合并后变成什么

\[0+0->0\\ 1+1->1\\ 0+1->x\\ 1+0->x\\ ?+1->1\\ ?+0->0\\ 1+?->1\\ 0+?->0\\ ?+?->? \]

现在就知道怎么合并了。

因为这个算法涉及到合并,那很容易就能够想到利用一下线段树。

因为每个字符串最多的话是 \(30\) 位。

这个时候可以有两种做法:

  1. \(30\) 棵线段树,大概是维护字符串每一位?
  2. 线段树每个叶子维护的是一个字符串,两个两个往上合并,上边的节点就是合并后的字符串。感觉第二种应该好理解,因为我用的是第二种。

我们合并的时候可以枚举,然后按照上边的合并方法合并出一个新的字符串。

void pushup (int now) {
	bz[now] = 0;
	if (bz[now << 1] || bz[now << 1 | 1]) {
		bz[now] = 1;
		return;
	}
	string tmp1 = s[now << 1], tmp2 = s[now << 1 | 1], tmp = "";
	for (int i = 0; i < n; ++ i) {
		if (tmp1[i] == '?') {
			if (tmp2[i] != '?') tmp += tmp2[i];
			else tmp += '?';
		}
		else if (tmp2[i] == '?') {
			if (tmp1[i] != '?') tmp += tmp1[i];
			else tmp += '?';
		}
		else if (tmp1[i] == tmp2[i]) tmp += tmp1[i];
		else {
			bz[now] = 1;
			return;
		}
	}
	s[now] = tmp;
}

我的合并方法是这样的,仅供参考。

bz[] 表示当前节点是不是有必要继续往上合并,因为如果出现 \(0,1\) 配对这种的就说明不存在一个可行的字符串了。

s[] 表示当前节点维护的区间的字符串。

然后最重要的东西就只有这个合并操作了,其他的没啥了。

代码:

# include <bits/stdc++.h>
using namespace std;
const int D = 1e5 + 10;
inline int read () {
	int x = 0, f = 1;
	char c = getchar ();
	while (c < '0' || c > '9') {if (c == '-') f = -1; c = getchar ();}
	while (c >= '0' && c <= '9') {x = x * 10 + c - '0', c = getchar ();}
	return x * f;
}
int n, m, q;
string s[D << 2], x[D];
bool bz[D << 2];
void pushup (int now) {
	bz[now] = 0;
	if (bz[now << 1] || bz[now << 1 | 1]) {
		bz[now] = 1;
		return;
	}
	string tmp1 = s[now << 1], tmp2 = s[now << 1 | 1], tmp = "";
	for (int i = 0; i < n; ++ i) {
		if (tmp1[i] == '?') {
			if (tmp2[i] != '?') tmp += tmp2[i];
			else tmp += '?';
		}
		else if (tmp2[i] == '?') {
			if (tmp1[i] != '?') tmp += tmp1[i];
			else tmp += '?';
		}
		else if (tmp1[i] == tmp2[i]) tmp += tmp1[i];
		else {
			bz[now] = 1;
			return;
		}
	}
	s[now] = tmp;
}
void build (int now, int l, int r) {
	if (l == r) {
		s[now] = x[l];
		return;
	}
	int mid = l + r >> 1;
	build (now << 1, l, mid);
	build (now << 1 | 1, mid + 1, r);
	pushup (now);
}
bool flag;
string query (int now, int l, int r, int L, int R) {
	if (flag) return "";
	if (L <= l && R >= r) {
		if (bz[now]) {
			flag = 1;
			return "";
		}
		return s[now];
	}
	int mid = l + r >> 1;
	string tmp1, tmp2, tmp = "";
	bool bj1 = 0,bj2 = 0;
	if (L <= mid) bj1=1,tmp1 = query (now << 1, l, mid, L, R);
	if (R >= mid + 1) bj2=1,tmp2 = query (now << 1 | 1, mid + 1, r, L, R);
	if (flag) return "";
	if (!bj1) return tmp2;
	if (!bj2) return tmp1;
	for (int i = 0; i < n; ++ i) {
		if (tmp1[i] == '?') {
			if (tmp2[i] != '?') tmp += tmp2[i];
			else tmp += '?';
		}
		else if (tmp2[i] == '?') {
			if (tmp1[i] != '?') tmp += tmp1[i];
			else tmp += '?';
		}
		else if (tmp1[i] == tmp2[i]) tmp += tmp1[i];
		else {
			flag = 1;
			return "";
		}
	}
	return tmp;
}
void update (int now, int l, int r, int x, string c) {
	if (l == r) {
		s[now] = "";
		s[now] += c;
		return;
	}
	int mid = l + r >> 1;
	if (x <= mid) update (now << 1, l, mid, x, c);
	else update (now << 1 | 1, mid + 1, r, x, c);
	bz[now] = 0;
	pushup (now);
}
int main () {
	cin >> n >> m >> q;
	for (int i = 1; i <= m; ++ i) cin >> x[i];
	build (1, 1, m);
	int ans = 0;
	int opt, l, r, p;
	string tmp;
	for (int i = 1; i <= q; ++ i) {
		opt = read ();
		if (opt == 0) {
			l = read (), r = read ();
			flag = 0;
			string tmp = query (1, 1, m, l, r);
			if (flag) continue;
			int num = 0;
			for (int j = 0; j < n; ++ j) num += tmp[j] == '?';
			ans ^= (1 << num);
		}
		else {
			tmp = "";
			p = read ();
			cin >> tmp;
			update (1, 1, m, p, tmp);
		}
	}
	cout << ans;
}

建议用 \(char\) ,要不比暴力还低。

润了,我不想改了。

posted @ 2022-10-24 23:01  zcxxxxx  阅读(45)  评论(2编辑  收藏  举报