2021ICPC沈阳 - B E F J

The 2021 ICPC Asia Shenyang Regional Contest

B 图论拆位染色
E 签到
F 字符串
J BFS

B. Bitwise Exclusive-OR Sequence

参考题解
将每一个数字视为图中一个点,对每一个限制建图连边带权
图中可能会出现重边和环,出现重边时,重边权值需相等;出现环时,环上边权异或值应该为 0
再在图上跑 dfs,在链上维护一个前缀异或和,同时统计每一个连通块
最后对每一个连通块考虑每一位怎么放置01使得1的放置次数最少即可

const int N = 1e5 + 5, inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f, mod = 998244353;
int n, m;
vector<pii> e[N];
bool vis[N], flag;
ll pre[N];// 统计前缀异或
vector<int> to[N];

void dfs(int u, int center){
	vis[center] = true;
	for(auto &[v, w] : e[center]){
		if(vis[v]){// 重边或者成环
			if((pre[center] ^ w) != pre[v]){
				flag = true;
				return ;
			}
		}else{
			pre[v] = pre[center] ^ w;
			to[u].push_back(v);
			dfs(u, v);
		}
	}
	return ;
}

ll calc(int u){
	int tot = to[u].size(), a = 0;
	for(int i = 0; i < 30; ++ i){
		int cnt = 0;
		for(auto v : to[u])
			if(pre[v] >> i & 1) ++ cnt;
		if(cnt > tot + 1 - cnt) a |= 1 << i;
	}
	ll ans = a;
	for(auto v : to[u])
		ans += pre[v] ^ a;
	return ans;
}

void solve(){
	cin >> n >> m;
	while(m --){
		int u, v, w;
		cin >> u >> v >> w;
		e[u].push_back({v, w});
		e[v].push_back({u, w});
	}
	ll ans = 0;
	for(int i = 1; i <= n; ++ i){
		flag = false;
		if(!vis[i]){
			dfs(i, i);
			if(flag){
				cout << -1 << '\n';
				return ;
			}
			ans += calc(i);
		}
	}
	cout << ans << '\n';
	return ;
}

30遍 dfs 做法

const int N = 1e5 + 5, M = 30 + 5, inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f, mod = 998244353;
int n, m, sum, vis[N], cnt[M], f[N];
bool flag;
vector<pii> e[N];

void dfs(int u, int val){
	f[u] = val;
	++ sum;
	vis[u] = true;
	for(int i = 0; i <= 30; ++ i)
		if(val >> i & 1) ++ cnt[i];
	for(auto & [v, w] : e[u]){
		if(vis[v]){
			if((val ^ w) != f[v]){
				flag = true; return ;
			}
		}else dfs(v, val ^ w);
	}
	return ;
}

void solve(){
	cin >> n >> m;
	for(int i = 0; i < m; ++ i){
		int u, v, w;
		cin >> u >> v >> w;
		e[u].push_back({v, w});
		e[v].push_back({u, w});
	}
	ll ans = 0;
	for(int i = 1; i <= n; ++ i){
		if(vis[i]) continue;
		mem(cnt, 0);
		sum = 0;
		flag = false;
		dfs(i, 0);
		if(flag){
			cout << -1 << '\n'; return ;
		}
		for(int i = 0; i <= 30; ++ i){
			ans += (ll)min(cnt[i], sum - cnt[i]) * (1 << i);// 注意long long
		}
	}
	cout << ans << '\n';
	return ;
}

E. Edward Gaming, the Champion

暴力找给定字符串出现的次数

void solve(){
	string ss;
	cin >> ss;
	int ans = 0;
	for(int i = 0; i < ss.size(); ++ i)
		if(ss.substr(i, 5) == "edgnb")
			++ ans;
	cout << ans << '\n';
	return ;
}

F. Encoded Strings I

从后往前考虑问题,如果某个字母第一次出现,那么接下来出现的相同字母都以它作为最后一个
从后往前统计所有答案取字典序最大的答案即可

void solve(){
	string ss, ans = "";
	int n;
	cin >> n >> ss;
	for(int i = 0; i < n; ++ i){
		vector<int> lei(26, -1);
		int cnt = 0;
		string t = "";
		for(int j = i; j >= 0; -- j){
			if(lei[ss[j] - 'a'] == -1){
				lei[ss[j] - 'a'] = cnt;
				t = (char)('a' + cnt) + t;
				++ cnt;
			}else t = (char)('a' + lei[ss[j] - 'a']) + t;
		}
		if(t > ans) ans = t;
	}
	cout << ans << '\n';
	return ;
}

J. Luggage Lock

注意到字符串 \(a_0a_1a_2a_3\) 通过操作变成 \(b_0b_1b_2b_3\) 等价于 字符串 \(0000\) 变成字符串 t,t 为 a 与 b 的差值字符串
故我们可以预处理出从字符串 0000 到所有字符串的最小操作次数,利用一个 BFS 即可,之后 O(1) 完成询问

map<string, int> state;

void pre(){
	string ss;
	queue<string> p;
	p.push("0000");
	state["0000"] = 0;
	while(p.size()){
		string t = p.front();
		p.pop();
		for(int i = 0; i < 4; ++ i)
			for(int j = 0; j < 4; ++ j){
				string up = t, down = t;
				for(int k = i; k <= j; ++ k){
					up[k] = (up[k] - '0' + 1) % 10 + '0';
					down[k] = (down[k] - '0' + 9) % 10 + '0';
				}
				if(!state.count(up)){
					state[up] = state[t] + 1;
					p.push(up);
				}
				if(!state.count(down)){
					state[down] = state[t] + 1;
					p.push(down);
				}
			}
	}
	return ;
}

void solve(){
	string s, t;
	cin >> s >> t;
	for(int i = 0; i < 4; ++ i){
		t[i] = (10 + t[i] - s[i]) % 10 + '0';
	}
	cout << state[t] << '\n';
	return ;
}
posted on 2023-11-16 15:38  Qiansui  阅读(9)  评论(0编辑  收藏  举报