The 2021 ICPC Asia Shenyang Regional Contest (B/E/F/J)

题目链接:Dashboard - The 2021 ICPC Asia Shenyang Regional Contest - Codeforces

目录

E. Edward Gaming, the Champion

F. Encoded Strings I

J. Luggage Lock

B. Bitwise Exclusive-OR Sequence


E. Edward Gaming, the Champion

题意:给你一个字符串,问该字符串中有多少个“edgnb”。

思路:签到题,直接遍历一遍字符串统计数目即可。

代码: 

void solve() {
	cin>>s;
	int ans=0;
	for(int i=0; i<(int)s.size()-4; i++) {
		if(s[i]=='e'&&s[i+1]=='d'&&s[i+2]=='g'&&s[i+3]=='n'&&s[i+4]=='b')ans++;
	}
	cout<

F. Encoded Strings I

题意:给你一个字符串s,字符串中只包含英文小写字母,每个小写字母的映射方式为:

设该小写字母最后一个出现位置为i,字符串长度范围为1~n,x为s[i]~s[n]中不同字符的个数,则小写字母被映射为字母表中的第x个数(比如x=1,则映射为 ‘a’;x=2,则映射为 ‘b’)。

例如:字符串“aacc”会被映射为“bbaa”,而字符串“aca”会被映射为“aba”,

求解目标为长度1~n的前缀中字典序最小的映射后的字符串。

思路: 因为映射看的是每一个字母最后一个出现的位置,所以我们从后往前遍历字符串,得出每个字母的映射字母,最后暴力枚举得出每个前缀字符串的映射字符串,sort取字典序最小即可。

代码:

void solve() {
	mapmp;
	vectorv;
	int n;
	string s;
	cin>>n>>s;
	for(int i=0; imp;
		int cnt=0;
		string k;
		for(int j=i; j>=0; j--) {
			if(mp[s[j]])k+=mp[s[j]];
			else mp[s[j]]='a'+cnt,k+=mp[s[j]],cnt++;
		}
		reverse(k.begin(),k.end());
		v.push_back(k);
	}
	sort(v.begin(),v.end());
	cout<

J. Luggage Lock

题意:给出一个4位密码锁,每次操作能滑动一个或多个相邻的位置向上或向下移动一格,现在给出多次询问,每次询问给出一个起点和终点,询问起点到终点的最少操作数。

思路: 我们可以先根据起点和终点的位置先得到他们的相对位置,比如起点为“1234”,终点为“2345”,那么他们的相对起点为“0000”,相对终点是“1111”,也就是他们两相减。这样,问题就转化成了起点“0000”到任意终点的最小操作数,这个最小操作数我们可以通过bfs来预处理,每次bfs标记走过的终点,并记录最小步数即可。

代码:

mapmp;
void bfs() {
	queue>q;
	string s="0000";
	q.push({s,0});
	while(!q.empty()) {
		string p=q.front().first;
		int step=q.front().second;
		q.pop();
		for(int i=0; i<4; i++) {
			for(int j=i; j<4; j++) {
				string t=p,tt=p;
				for(int k=i; k<=j; k++) {
					if(t[k]=='9')t[k]='0';
					else t[k]++;
					if(tt[k]=='0')tt[k]='9';
					else tt[k]--;
				}
				if(!mp[t]&&t!="0000")mp[t]=step+1,q.push({t,step+1});
				if(!mp[tt]&&tt!="0000")mp[tt]=step+1,q.push({tt,step+1});
			}
		}
	}
}
void solve() {
	string kk,a,b;
	cin>>a>>b;
	for(int i=0; i<4; i++) kk+=(b[i]-a[i]+10)%10+'0';
    cout<

B. Bitwise Exclusive-OR Sequence

题意:给你一个n,表示有n个点,给你m个a,b,w,表示点a的权值和点b的权值异或值为w,求有可能的点权中最小的点权之和。

思路:因为对于二进制来说,每个位数的异或是独立的,是互不影响的,并且若已知一个连通块中某个点权的某个二进制位值,那么其它点权的该二进制位值就都知道了,所以只需枚举各个连通块中随便的一个点权的二进制位即可,并且二进制是0的情况和二进制是1的情况是对称的,取min累加即可,具体实现见代码与注释。

代码:

vector e[maxn];
bool st[maxn];
int n,m,ans=0,fa[maxn],sum[maxn],res[40],now[maxn];
//sum用来记录连通块大小,res用来记录各个位上的1的个数之和,now来记录当前节点的权值 
int find(int x) {//并查集用来记录每个连通块的大小 
	if(x!=fa[x])return fa[x]=find(fa[x]);
	return x;
}
void dfs(int u) {
	st[u]=true;
	for(auto x:e[u]) {
		int a=x.first,b=x.second;
		if(st[a]) {//如果已经走过了,则看看是否会发生冲突,若会发生冲突则输出-1 
			if((now[a]^now[u])!=b) {
				cout<<-1<>i)&1);// 将各个位上的1加入总和中 
			dfs(a);
		}
	}
}
void solve() {
	cin>>n>>m;
	for(int i=1; i<=n; i++)fa[i]=i,sum[i]=1;
	for(int i=1; i<=m; i++) {
		int x,y,w;
		cin>>x>>y>>w;
		e[x].push_back({y,w});
		e[y].push_back({x,w});
		int fx=find(x),fy=find(y);
		if(fx!=fy)sum[fx]+=sum[fy],fa[fy]=fx;//计算连通块大小 
	}
	for(int i=1; i<=n; i++) {
		if(find(i)!=i)continue;//一个连通块只跑一次 
		for(int j=0; j<=30; j++)res[j]=0;
		dfs(i);
		for(int j=0; j<=30; j++)ans+=min(res[j],sum[i]-res[j])*(1<

你可能感兴趣的:(区域赛题解,算法)