CSP 日照集训考试 Day3
考的还不错,但是没干过内裤。
T1
好一眼啊这个题。
化个式子就做出来了。
然后要求的是若干个正整数解,所以 。
因为 都是正整数。
所以 一定是一个正数,那想要让 ,那么 。
则 。
然后就枚举 求 即可。
然后这个 ,所以复杂度很正确。
# 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
想了老长时间了。
貌似是先打完了 ,再想出这个题正解的。
我一开始以为是个线段树的题。
异或这一类题有几个很重要的东西,一个是交换律,一个就是前缀和。
那这个题就是考的前缀和。
我一开始想的思路:
sum[]
表示的是前缀异或和。
我先考虑修改某个位置比如说 吧,那 一直到 都会被影响到。
那我想呢,用一个线段树维护一下这个前缀异或和。
然后修改的时候从这个地方到结尾整体改一下。
接着就没思路了…想了好几种办法,都不可行,不可行指的是复杂度貌似不对。
然后去想 了。
写完 ,回来写 ,我觉得实在不行了就打个暴力吧。
然后打着打着……
发现。
貌似进行一次操作对前缀和的影响只是对 这个位置产生影响诶。
然后切了。
具体点的思路:
因为要让某个区间异或和等于零,即 ^ 。
所以只要两个位置的前缀数组的值一样就 ok 。
我们先不考虑别的,先想这个原数组的答案怎么算。
我一开始想了不少方法,有组合数之类的。但是貌似不太行,因为要预处理阶乘,而且这个预处理到什么程度也不知道。
所以,我们考虑一个数对其他数的贡献。一个区间需要两个位置就能确定,所以一个位置对其他位置的贡献就是跟这个位置 值相等的位置的个数,然后会发现,会重复计算贡献,所以出来答案的时候除以 。
考虑删掉和加上某个数产生的贡献。
想要删掉这个位置的数,那他与其他位置构成的区间的个数就要减掉;因为一开始记录答案的时候是算的重复的,所以其他位置与这个位置构成的区间个数也要减掉。
想要把这个位置异或上一个数,也就是添一个新的数,那他对答案的贡献就是把他的值改变后会有多少个跟他相等的数,然后其他的位置对这个位置的贡献也要重复加一下。
没啥了。代码:
# 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
一眼线段树题。
题目中给定几个字符串,我们可以把他们看成若干条件。
例如 和 。
需要让我们找出若干个字符串与这两个字符串匹配。
很容易就能发现,想要同时满足这两个条件,那就需要把这两个条件合并。
只要满足合并后的条件,那就一定满足这两个条件中任意的一个。
所以关键在于怎么合并条件。
可以按照以下的方式:
每一行分别表示:前一个字符串上的某一位;第二个字符串上对应的位置;合并后变成什么
现在就知道怎么合并了。
因为这个算法涉及到合并,那很容易就能够想到利用一下线段树。
因为每个字符串最多的话是 位。
这个时候可以有两种做法:
- 开 棵线段树,大概是维护字符串每一位?
- 线段树每个叶子维护的是一个字符串,两个两个往上合并,上边的节点就是合并后的字符串。感觉第二种应该好理解,因为我用的是第二种。
我们合并的时候可以枚举,然后按照上边的合并方法合并出一个新的字符串。
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[]
表示当前节点是不是有必要继续往上合并,因为如果出现 配对这种的就说明不存在一个可行的字符串了。
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;
}
建议用 ,要不比暴力还低。
润了,我不想改了。
本文作者:zcxxxxx
本文链接:https://www.cnblogs.com/zcxxxxx-blog/p/16823394.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】