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 ;
}