D
three days ago
题目大意:当一个由数字组成的字符串可以重新排列成(或已经是)某个字符串的重复两次时,它被认为是快乐的。您将获得一个由数字组成的字符串。查找满足以下所有条件的整数对数。
我们发现只要在某一个子段中,每种字符出现的次数都为偶数时,这个子段便是满足条件的。
那么,问题来了,怎么判断某一子段中各个字符出现的次数是不是偶数呢?
这里我们可以这样处理:我们对每一个字符出现的次数,它要么是奇数,要么是偶数,那么我们可以用一个二进制位表示该字符的状态,0表示出现次数为偶数,1表示出现次数为奇数,10位即可表示所有字符出现次数。这样一来,我们只需要10个二进制位就可以表示0-9各个数字的状态,开一个1<<10大小的数组就可以记录所有状态了。每一个状态转变只需要上一个状态state^(1<<(s[i]-'0'))即可。
例如20230322,我们记录一下前i个数所代表的状态:
i=0 0000000000
i=1 0000000100
i=2 0000000101
i=3 0000000001
i=4 0000001001
i=5 0000001000
i=6 0000000000
i=7 0000000100
i=8 0000000000
当两个状态相等时,则它们之间的部分恰好满足条件,那么我们只需要不断遍历字符串,把每个状态出现的次数记下来,并且每次让ans加上这个状态出现的次数即可。例如某个状态已经出现过3次,那第四次出现时,就先加ans+3,再让次数times+1;即该位置与前面三个位置分别构成3个符合条件的子串。
代码如下:
#include <bits/stdc++.h>
using namespace std;
const int maxn=5e5+10;
char s[maxn];
int t[1<<10]={1},tmp;
int main(){
string s;
cin >> s;
long long ans=0;
for(int i=0;i<s.size();i++){
tmp^=1<<(s[i]-'0');
ans+=t[tmp]++;
}
cout << ans << endl;
return 0;
}
G
Minimum Reachable City
我们有一个有向图,顶点编号为1~n 。 它有n-1条边,第i条边从pi到i+1。
有两种类型的查询,如下所示。
1 u v:添加一个从顶点到顶点的边。2 x:打印可从顶点通过某些边(包括顶点)到达的顶点的最小顶点数。
并查集
那么我们可以设立一个数组存储每一个结点的祖先,并把它设为该点所能到达的最小点,可以联想到用并查集的思想,把拥有相同祖先的点收到一个集合里。
首先读取n-1个数据,将他们存为对应点的父节点,存到一个fa数组里,之后每读入1 u v,将两个点以及他们的父节点所在的集合并起来,并且找到他们所到达的最小点设为祖先,之后2询问时,只需要输出find(x)即可。
代码:
#include <bits/stdc++.h>
using namespace std;
const int maxn=2e5+10;
int f[maxn],fa[maxn];
int find(int x){
return f[x]==x?x:f[x]=find(f[x]);
}
void merge(int a,int b){//合并两个点所在的集合
int u=find(a),v=find(b);
if(u<v) f[v]=u;
else f[u]=v;
}
int main(){
int n;
cin >> n;
for(int i=1;i<=n;i++){
f[i]=i;
}
for(int i=2;i<=n;i++){
int d;
cin >> d;
fa[i]=d;
}
int q;
cin >> q;
while(q--){
int op;
cin >> op;
if(op==1){
int a,b;
cin >> a >> b;
int u=find(a);
while(u>b) {//向上找父亲,并找到它们对应的可到达的最小点
f[u]=find(fa[u]);
u=find(u);
}
}
else {
int t;
cin >> t;
cout << find(t) << endl;
}
}
return 0;
}
本文介绍了如何使用C++解决atcoder Beginner Contest 295的D题(Three Days Ago)和G题(Minimum Reachable City)。D题关键在于判断字符串子段中字符出现次数是否均为偶数,利用二进制位表示状态进行求解。G题则涉及有向图的最小可达顶点问题,采用并查集的数据结构来解答。

235

被折叠的 条评论
为什么被折叠?



