【题解】Educational Codeforces Round 2

因为计算几何题不会所以就没有

A.Extract Numbers

题目描述

原题面

题目分析

注意到所有的单词通过 ,; 分割,可能会有空的单词。我感觉比较好的写法是把每个单词提取出来,对于两种字符串分别搞两个 vector 然后插入就好了。字符串模拟也没啥说的

代码详解

点击查看代码
#include<bits/stdc++.h>
using namespace std;
string s;
vector<string> a,b;
int main(){
	cin>>s;
	s = s + ',';
	for(int i=0; i<s.size();){
		string tmp = "";
		for(; i < s.size() && s[i] != ',' && s[i]!=';'; i++){
			tmp = tmp + s[i];
		}
		if(tmp == "")
			b.push_back(tmp);
		else if(tmp == "0")
			a.push_back(tmp);
		else if(tmp[0] == '0')
			b.push_back(tmp);
		else{
			bool flag = true;
			for(int j=0; j<tmp.size(); j++){
				if(tmp[j] < '0' || tmp[j] > '9'){
					flag = false;
					break;
				}
			}
			if(flag)
				a.push_back(tmp);
			else
				b.push_back(tmp);
		}
		i++;
	}
	if(a.size() >= 1){
		cout<<'"';
		for(int i=0; i<a.size(); i++){
			cout<<a[i];
			if(i != a.size() - 1)
				cout<<',';
		}
		cout<<'"';
	}
	else{
		printf("-");
	}
	printf("\n");
	if(b.size() >= 1){
		cout<<'"';
		for(int i=0; i<b.size(); i++){
			cout<<b[i];
			if(i != b.size() - 1)
				cout<<',';
		}
		cout<<'"';
	}
	else{
		printf("-\n");
	}
	return 0;
}

我们先给 \(s\) 加上一个分隔符,因为最后的一个分隔符会被忽略掉。

B.Queries about less or equal elements

题目描述:

原题面

题目分析:

就是每次查询 \(a\) 中比当前元素小的元素个数,就很简单地搞一个值域线段树,然后将所有的数值提前离散化一下,然后就线段树维护区间和了,就是很基本的了

代码详解:

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 4e5+5;
int tree[4 * MAXN],a[MAXN],b[MAXN],p[3 * MAXN],tot,cnt;
map<int,int> mp;
void update(int now){
	tree[now] = tree[now<<1] + tree[now<<1|1];
}
void add(int now,int now_l,int now_r,int pos,int k){
	if(now_l == now_r){
		tree[now] += k;
		return;
	}
	int mid = (now_l + now_r)>>1;
	if(pos <= mid)	add(now<<1,now_l,mid,pos,k);
	else	add(now<<1|1,mid+1,now_r,pos,k);
	update(now);
}
int query(int now,int now_l,int now_r,int l,int r){
	if(l <= now_l && r >= now_r){
		return tree[now];
	}
	int mid = (now_l + now_r)>>1,ans = 0;
	if(l <= mid)	ans += query(now<<1,now_l,mid,l,r);
	if(r > mid)		ans += query(now<<1|1,mid+1,now_r,l,r);
	return ans;
}
int main(){
	int n,m;
	cin>>n>>m;
	for(int i=1; i<=n; i++){
		cin>>a[i];
		p[++tot] = a[i];
	}
	for(int i=1; i<=m; i++){
		cin>>b[i];
		p[++tot] = b[i];
	}
	sort(p+1,p+tot+1);
	for(int i=1; i<=tot; i++){
		if(!mp.count(p[i])){
			mp[p[i]] = ++cnt;
		}
	}
	for(int i=1; i<=n; i++){
		add(1,1,cnt + 3,mp[a[i]],1);
	}
	for(int i=1; i<=m; i++){
		printf("%d ",query(1,1,cnt + 3,1,mp[b[i]]));
	}
	return 0;
}

C.Make Palindrome

题目描述:

原题面

题目分析:

因为排列这个字符串不算做操作次数,所以我们不用在意这些字符的排列方式,只需要记录一下这个字符串里每个字母有多少个就可以了。
考虑一个回文串就是最多只有一个字母的个数是奇数,为了使得操作最小我们就只能将奇数的字母变化为奇数的字母,这样就可以将这两个字母变成偶数也就是可以回文了。
为了让最后的字典序最小很显然我们要将尽量大的数变成尽量小的数,就直接逆序然后顺序扫就可以了。注意输出的时候要按照字典序将字母从小到大按规定个数输出,如果存在字母个数为奇数要在中间输出

代码详解:

点击查看代码
#include<bits/stdc++.h>
using namespace std;
int cnt[26];
int main(){
	string s;
	cin>>s;
	for(int i=0; i<s.size(); i++){
		cnt[s[i] - 'a']++;
	}
	for(int i=25; i>=0; i--){
		if(cnt[i] & 1){
			for(int j=0; j<=25; j++){
				if(cnt[j] & 1){
					cnt[j]++;
					cnt[i]--;
					break;
				}
			}
		}
	}
	int flag = -1;
	for(int i=0; i<=25; i++){
		if(cnt[i] & 1){
			flag = i;
		}
	}
	stack<char> st;
	for(int i=0; i<=25; i++){
		for(int j=1; j<=cnt[i]/2; j++){
			cout<<char(i + 'a');
			st.push(char(i + 'a'));
		}
	}
	if(flag != -1){
		cout<<char(flag + 'a');
	}
	while(!st.empty()){
		cout<<st.top();
		st.pop();
	}
	return 0;
}

E.Lomsat gelral

题目描述:

原题面

题目分析:

这是一个非常经典的问题:我们需要在树上维护一个不能高效合并的信息。既然不能高效合并那么我们就暴力搜索每一棵子树得到每一个子树的答案就好了,但是暴力也不能纯纯地直接去搜也要有一些策略。
我们的暴力过程就是来到一棵树,获取这棵树的所有子树的信息然后清空去下一棵子树,因为我们最后一定会去获取当前树的信息,所以我们可以最后留一棵子树的信息不去清空,而是直接当作当前树已知的部分信息,再去搜索当前树其他的子树就好了。我们为了尽可能优化时间,最后不清空信息的这棵子树为当前点的重儿子,也就是大小最大的那棵子树,显然最优。
这也就是树上启发式合并,可以证明我们每个节点被访问的次数不会超过 \(\log n\) 级别,因为我们每个点的修改信息的复杂度都是 \(O(1)\) 的,所以我们时间复杂度就是 \(O(n\log n)\),非常优秀

代码详解:

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const long long MAXN = 1e5+5;
const long long MAXM = 2 * MAXN;
struct edge{
	long long nxt,to;
	edge(){}
	edge(long long _nxt,long long _to){
		nxt = _nxt,to = _to;
	}
}e[MAXM];
long long tot,mx,sum,head[MAXN],sz[MAXN],son[MAXN],color[MAXN],cnt[MAXN],ans[MAXN];
void add_edge(long long from,long long to){
	e[++tot] = edge(head[from],to);
	head[from] = tot;
}
void get_son(long long now,long long fa){   //获取子树大小以及重儿子 
	sz[now] = 1;
	long long maxn = 0;
	for(long long i=head[now]; i; i = e[i].nxt){
		long long to = e[i].to;
		if(to == fa)	continue;
		get_son(to,now);
		if(sz[to] > sz[maxn]){
			maxn = to;
		}
		sz[now] = sz[now] + sz[to];
	}
	son[now] = maxn;
}
void get_ans(long long now,long long fa,long long heavy_son){   //暴力 dfs 当前子树的信息,除去重儿子 
	cnt[color[now]]++;
	if(cnt[color[now]] > mx){
		mx = cnt[color[now]];
		sum = color[now];
	}
	else if(cnt[color[now]] == mx){
		sum += color[now];
	}
	for(long long i=head[now]; i; i = e[i].nxt){
		long long to = e[i].to;
		if(to == fa || to == heavy_son)	continue;
		get_ans(to,now,heavy_son);
	}
}
void clear(long long now,long long fa){  //清空当前子树的信息 
	cnt[color[now]]--;
	for(long long i=head[now]; i;i = e[i].nxt){
		long long to = e[i].to;
		if(to == fa)	continue;
		clear(to,now);
	}
}
void dfs(long long now,long long fa){   //启发式合并的过程 
	for(long long i=head[now]; i;i = e[i].nxt){
		long long to = e[i].to;
		if(to == fa	|| to == son[now])	continue;
		dfs(to,now);
		clear(to,now);
		sum = mx = 0;
	}
	if(son[now]){  //重儿子不清空 
		dfs(son[now],now);
	}
	get_ans(now,fa,son[now]); 
	ans[now] = sum;
}
int main(){
	long long n;
	cin>>n;
	for(long long i=1; i<=n; i++){
		cin>>color[i];
	}
	for(long long i=1; i<n; i++){
		long long from,to;
		cin>>from>>to;
		add_edge(from,to);
		add_edge(to,from);
	}
	get_son(1,0);
	dfs(1,0);
	for(long long i=1; i<=n; i++){
		printf("%lld ",ans[i]);
	}
	return 0;
}

也基本不需要说什么了,毕竟本质上也只是暴力而已。

F.Edge coloring of bipartite graph

题目描述:

原题面

题目分析:

这个也就是对一个二分图进行边染色,很显然可以发现:颜色数量就是度数最大的点的度数。设最大点度数为 \(k\),则在二分图中 \(ans = k\) 而在简单图中 \(k \le ans \le k + 1\)
我们每次选择一条边,然后枚举这条边所连接的两个点的没有使用到的颜色的最小值,然后拿这个最小值然当前的边。可能这两个最小值不同,那么就在二分图中用这两个颜色交替染色最后染出来的也就是正确的了。

代码详解:

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2e3+5;
const int MAXM = 1e5+5;
struct node{
	int from,to;
	node(){}
	node(int _from,int _to){
		from = _from,to = _to;
	}
}a[MAXM];
int du[MAXN],color[MAXN][MAXN];
int main(){
	int l,r,m;
	scanf("%d%d%d",&l,&r,&m);
	for(int i=1; i<=m; i++){
		scanf("%d%d",&a[i].from,&a[i].to);
		a[i].to += l;
		du[a[i].from]++;
		du[a[i].to]++;
	}
	int ans = 0;
	for(int i=1; i<=l + r; i++){
		ans = max(ans,du[i]);
	}
	for(int i=1; i<=m; i++){
		int from = a[i].from,to = a[i].to;
		int color1 = 1,color2 = 1;
		while(color[from][color1])	color1++;
		while(color[to][color2])	color2++;
		color[from][color1] = to;
		color[to][color2] = from;
		if(color1 == color2)	continue;
		for(int tmp = color2,w = to;w ; w = color[w][tmp],tmp ^= color1 ^ color2){   //异或异或等于没异或所以相当于交换 
			swap(color[w][color1],color[w][color2]);
		}
	}
	printf("%d\n",ans);
	for(int i=1; i<=m; i++){
		for(int j=1; j<=ans; j++){
			if(color[a[i].from][j] == a[i].to){
				printf("%d ",j);
				break;
			}
		}
	}
	return 0;
}

posted @ 2022-06-24 13:27  linyihdfj  阅读(104)  评论(0编辑  收藏  举报