Loading

The 2021 ICPC Asia Shenyang Regional Contest



B - Bitwise Exclusive-OR Sequence

题意

\(n\)个数,\(m\)个关系,每个关系形如 \(a_u⊕a_v=w\),表示第\(u\)个数与第\(v\)数的异或运算结果为\(w\)。求是否有这样的\(n\)个数满足所有关系要求,如果没有输出\(-1\),如果有输出所有满足要求的方案中,所有数字的和的最小值。

思路

建图,处理每个连通块,选取源点赋值为\(0\)(因为要求最小),途中出现矛盾直接输出\(-1\),否则从低位到高位统计每一位上\(0\)\(1\)的出现次数,最后相加计算最小值。

代码

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long

const int mxn = 1e6 + 5;

int n, m, idx;
int to[mxn], nxt[mxn], head[mxn], edge[mxn], point[mxn], pre[mxn], sz[mxn];

void add(int u, int v, int w)
{
	to[++idx] = v;
	edge[idx] = w;
	nxt[idx] = head[u];
	head[u] = idx;
}

void init()
{
	fill(point, point + n, -1);
	fill(sz, sz + n, 1);
	iota(pre, pre + n, 0);
}

int find(int x)
{
	return (x == pre[x] ? x : pre[x] = find(pre[x]));
}

void join(int u, int v)
{
	int fu = find(u);
	int fv = find(v);
	if (fu != fv)
	{
		pre[fu] = fv;
		sz[fv] += sz[fu];
	}
}

void solve()
{
	cin >> n >> m;
	init();
	if (!m)
	{
		cout << 0 << endl;
		return;
	}
	for (int i = 0; i < m; i++)
	{
		int u, v, w;
		cin >> u >> v >> w;
		u--, v--;
		add(u, v, w);
		add(v, u, w);
		join(u, v);
	}
	int ans = 0;
	for (int i = 0; i < n; i++)
	{
		if (find(i) != i)
		{
			continue;
		}
		vector<int> cnt(30);
		queue<int> q;
		q.push(i);
		point[i] = 0;
		while (q.size())
		{
			int u = q.front();
			q.pop();
			for (int j = head[u]; j; j = nxt[j])
			{
				int v = to[j];
				if (point[v] == -1)
				{
					point[v] = (point[u] ^ edge[j]);
					q.push(v);
					for (int k = 0; k < 30; k++)
					{
						cnt[k] += ((point[v] >> k) & 1); // 统计1的个数
					}
				}
				else
				{
					if ((point[u] ^ point[v]) != edge[j]) // 前后矛盾
					{
						cout << -1 << endl;
						return;
					}
				}
			}
		}
		for (int j = 0; j < 30; j++)
		{
			ans += (1LL << j) * min(cnt[j], sz[i] - cnt[j]); // 二进制加法,cnt是1的个数,sz-cnt是0的个数,min(cnt[j], sz[i] - cnt[j])其实是当前连通快中第j位的贡献
		}
	}
	cout << ans << endl;
}

signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);

	int T = 1;
	//cin >> T;
	while (T--)
	{
		solve();
	}

	return 0;
}

E - Edward Gaming, the Champion

题意

给个字符串,找有几个\(edgnb\)

思路

模拟。

代码

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long

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

signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);

	int T = 1;
	//cin >> T;
	while (T--)
	{
		solve();
	}

	return 0;
}

F - Encoded Strings I

题意

给定一个长度为\(n(n≤1000)\)的字符串\(s\),定义函数\(Fs:Fs(c)=chr(G(c, S))\),表示将\(S\)中的每个字符\(c\)转换为\(chr(G(c, S))\),其中\(G(c, S)\)表示\(S\)中最后一次出现\(c\)之后的后缀中不同的字符个数,\(chr(i)\)表示第\(i\)个字符(第个\(0\)字符是 \(a\),第\(1\)个是\(b\)…)。要求你对\(s\)的每个非空前缀代入到函数中,得到\(n\)个字符串,输出按照字典序排序的最大字符串。

思路

模拟。

代码

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long

const int mxn = 1e3 + 5;

unordered_map<char, int> cnt[mxn];

void solve()
{
	int n;
	string s;
	cin >> n >> s;
	for (int i = n; i >= 1; i--)
	{
		int cntt = 0;
		unordered_set<char> vis;
		for (int j = i - 1; j >= 0; j--)
		{
			if (!vis.count(s[j]))
			{
				vis.insert(s[j]);
				cnt[i][s[j]] = cntt++;
			}
		}
	}
	set<string, greater<string>> ans;
	for (int i = n; i >= 1; i--)
	{
		string t = s.substr(0, i);
		for (int j = i - 1; j >= 0; j--)
		{
			t[j] = cnt[i][t[j]] + 'a';
		}
		ans.insert(t);
	}
	cout << *ans.begin() << endl;
}

signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);

	int T = 1;
	//cin >> T;
	while (T--)
	{
		solve();
	}

	return 0;
}

J - Luggage Lock

题意

四位密码锁,每次能把一位或连续的几位向上或向下转动一个单位。现给两个密码\(A\)\(B\),求从\(A\)\(B\)的最少操作数。

思路

\(T\)挺大,一个个来铁超时,直接从\(0000\)开始用\(bfs\)打表,查的时候就可以等价于查\(0000\)\(A\)\(B\)之间的差值,即查\(1234\)\(4689\)相当于查\(0000\)\(3455\)

代码

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long

typedef pair<string, int> pii;

map<string, int> ans;
set<string> vis;

string cmd[] = { "1000","0100", "0010", "0001", "1100",   "0110", "0011", "1110","0111", "1111", "2000","0200", "0020", "0002", "2200",  "0220",  "0022", "2220", "0222", "2222" }; // 1代表+,2代表-

string change(string s, int idx)
{
	string res = s;
	for (int i = 0; i < 4; i++)
	{
		if (cmd[idx][i] == '1')
		{
			res[i]++;
			if (res[i] > '9')
			{
				res[i] = '0';
			}
		}
		else if (cmd[idx][i] == '2')
		{
			res[i]--;
			if (res[i] < '0')
			{
				res[i] = '9';
			}
		}
	}
	return res;
}

void bfs()
{
	queue<pii> q;
	q.push({ "0000", 0 });
	vis.insert("0000");
	while (q.size())
	{
		string cur = q.front().first;
		int cnt = q.front().second;
		q.pop();
		for (int i = 0; i < 20; i++) // 把cur的全部情况都存了
		{
			string t = change(cur, i);
			if (!vis.count(t))
			{
				vis.insert(t);
				ans[t] = cnt + 1;
				q.push({ t, cnt + 1 });
			}
		}
	}
}

void solve()
{
	string a, b;
	cin >> a >> b;
	if (a == b)
	{
		cout << 0 << endl;
		return;
	}
	string t = "";
	for (int i = 0; i < 4; i++)
	{
		t.push_back((b[i] - a[i] + 10) % 10 + '0');
	}
	cout << ans[t] << endl;
}

signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);

	int T = 1;
	cin >> T;
	bfs();
	while (T--)
	{
		solve();
	}

	return 0;
}

H - Line Graph Matching

题意

给定简单无向加权连通图\(G\),计算其线图\(L(G)\)的最大加权匹配中所有边的权重之和。
线图\(L(G)\):每个顶点代表\(G\)的一条边,\(L(G)\)中的边表示\(G\)中两条边共享一个端点的情况,其权重为这两条边权重的和。
最大加权匹配:一种边的集合,其中任意两条边没有共同的顶点,且该集合中边的权重之和最大化。

思路

对于每个连通块,如果边数为偶数,一定能匹配完;否则,要删一条边,如果删的是割边,要保证删完两边连通块边数都是偶数,删的不是割边则没有限制。
一种思路是用\(tarjan\)求割边,如果要删就删最小边(戳这里看参考博客),还有一种思路是贪心,每次选权最大的边加入(戳这里看参考博客)。
这就是🥈题,\(What\ can\ I\ say\)

代码一

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long

const int mxn = 1e6 + 5;

int n, m, idx, tot, ans, minn, ttot;
int to[mxn], nxt[mxn], head[mxn], edge[mxn], dfn[mxn], low[mxn], sz[mxn], deg[mxn];

void add(int u, int v, int w)
{
	to[++idx] = v;
	edge[idx] = w;
	nxt[idx] = head[u];
	head[u] = idx;
}

void tarjan(int u, int pre)  
{
    dfn[u] = low[u] = ++tot;
    sz[u] = deg[u];
    for (int i = head[u]; i; i = nxt[i])
    {
        int v = to[i];
        if (!dfn[v])
        {
            tarjan(v, u);  
            sz[u] += sz[v];
            low[u] = min(low[u], low[v]);
            if (low[v] > dfn[u])  // 割边
            {
                if ((sz[v] - 1) / 2 % 2 == 0)  // 删边后边数是偶数
                {
                    minn = min(minn, edge[i]);
                }
            }
            else
            {
                minn = min(minn, edge[i]);
            }
        }
        else if (v != pre)
        {
            low[u] = min(low[u], dfn[v]);
            minn = min(minn, edge[i]);
        }
        if (dfn[u] < dfn[v]) // 该连通块内的边数
        {
            ttot++;
        }
    }
}

void solve()
{
    cin >> n >> m;
    for (int i = 1; i <= m; i++)
    {
        int u, v, w;
        cin >> u >> v >> w;
        add(u, v, w);
        add(v, u, w);
        deg[u]++;
        deg[v]++;
        ans += w;
    }
    for (int i = 1; i <= n; i++)
    {
        if (!dfn[i])
        {
            ttot = 0;
            minn = LLONG_MAX;
            tarjan(i, -1);
            if (ttot & 1) // 奇数要删一条最小边
            {
                ans -= minn;
            }
        }
    }
    cout << ans << endl;
}


signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);

	int T = 1;
	//cin >> T;
	while (T--)
	{
		solve();
	}

	return 0;
}

代码二

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long

const int mxn = 1e6 + 5;

struct edge
{
    int u, v, w;
    bool operator < (const edge& a) const
    {
        return w > a.w;
    }
};

int n, m, ans;
vector<edge> e;
int pre[mxn], w[mxn]; 

int find(int x)
{
    return (x == pre[x] ? x : pre[x] = find(pre[x]));
}

void solve()
{
    cin >> n >> m;
    iota(pre + 1, pre + 1 + n, 1);
    e.resize(m + 1);
    for (int i = 1; i <= m; i++)
    {
        cin >> e[i].u >> e[i].v >> e[i].w;
    }
    sort(e.begin() + 1, e.end()); // 按边权降序排序
    for (int i = 1; i <= m; i++)
    {
        int fu = find(e[i].u);
        int fv = find(e[i].v);
        if (fu == fv)
        {
            if (!w[fu])
            {
                w[fu] = e[i].w;
            }
            else
            {
                ans += e[i].w + w[fu];
                w[fu] = 0;
            }
        }
        else
        {
            pre[fv] = fu;
            if (!w[fu] && !w[fv])
            {
                w[fu] = e[i].w;
            }
            else if (!w[fu] || !w[fv])
            {
                ans += e[i].w + w[fu] + w[fv];
                w[fu] = 0;
            }
            else
            {
                ans += e[i].w + max(w[fu], w[fv]);
                w[fu] = min(w[fu], w[fv]);
            }
        }      
    }
    cout << ans << endl;
}


signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);

	int T = 1;
	//cin >> T;
	while (T--)
	{
		solve();
	}

	return 0;
}


比赛链接 https://codeforces.com/gym/103427

posted @ 2024-10-03 16:37  _SeiI  阅读(3)  评论(0编辑  收藏  举报