2021 ICPC 沈阳区域赛 BEFHJL

B - Bitwise Exclusive-OR Sequence

种类并查集。

根据每一对的异或关系,可以得到二进制中每一位是否互斥关系,涉及到两种关系的处理用种类并查集更好维护;另外再维护两个点之间是否有关系,之后可能形成多个关系的集合,每个集合又分成了互斥的两个部分,对少的那一堆赋值为 \(1\) 即可。

点击查看代码
#include <bits/stdc++.h>

using namespace std;

using i64 = long long;

struct DSU {
	std::vector<int> f, siz;

	DSU() {}
	DSU(int n) {
		init(n);
	}

	void init(int n) {
		f.resize(n);
		std::iota(f.begin(), f.end(), 0);
		siz.assign(n, 1);
		for (int i = n / 2 + 1; i < n; i += 1) {
			siz[i] = 0;
		}
	}

	int find(int x) {
		while (x != f[x]) {
			x = f[x] = f[f[x]];
		}
		return x;
	}

	bool same(int x, int y) {
		return find(x) == find(y);
	}

	bool merge(int x, int y) {
		x = find(x);
		y = find(y);
		if (x == y) {
			return false;
		}
		siz[x] += siz[y];
		f[y] = x;
		return true;
	}

	int size(int x) {
		return siz[find(x)];
	}
};

int main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);

	int n, m;
	cin >> n >> m;

	vector<DSU> dsu(30, DSU(2 * n + 1));

	vector<array<int,3>> e(m);
	vector g(n + 1, vector<int>());
	for (auto &[u, v, w] : e) {
		cin >> u >> v >> w;
		for (int i = 29; i >= 0; i -= 1) {
			if (w >> i & 1) {
				if (dsu[i].same(u, v) || dsu[i].same(u + n, v + n)) {
					cout << "-1\n";
					return 0;
				}
				dsu[i].merge(u + n, v);
				dsu[i].merge(u, v + n);
			}
			else {
				if (dsu[i].same(u, v + n) || dsu[i].same(u + n, v)) {
					cout << "-1\n";
					return 0;
				}
				dsu[i].merge(u, v);
				dsu[i].merge(u + n, v + n);
			}
		}

		g[u].push_back(v);
		g[v].push_back(u);
	}


	i64 ans = 0;
	for (int i = 29; i >= 0; i -= 1) {
		i64 k = 0;

		vector<int> vis(2 * n + 1), has(2 * n + 1);
		auto bfs = [&](int x)->int{
			queue<int> Q;
			Q.push(x);

			int res = n, cnt = 0;

			while (Q.size()) {
				auto u = Q.front();
				Q.pop();

				if (vis[u]) {
					continue;
				}
				vis[u] = 1;

				if (!has[dsu[i].find(u)]) {
					cnt += 1;
					has[dsu[i].find(u)] = 1;
					res = min(res, dsu[i].size(u));
				}

				for (auto &v : g[u]) {
					if (!vis[v]) {
						Q.push(v);
					}
				}
			}

			return (cnt > 1 ? res : 0);
		};

		for (int j = 1; j <= n; j += 1) {
			if (vis[j]) {
				continue;
			}
			k += bfs(j);
		}

		ans += (1LL << i) * k;
	}

	cout << ans << "\n";

	return 0;
}

E - Edward Gaming, the Champion

模拟。

签到题,一个典型的错法是:

string s; cin >> s;
for (int i = 0; i < s.size() - 4; i++) { ... }

\(\text{str.size()\ <\ 4}\) 会导致 \(\text{size\_t}\) 类型的下溢出,并且在 \(\text{for}\) 循环判断退出条件时 \(\text{int}\) 会被 \(\text{static\_cast}\)\(\text{size\_t}\) 类型进行比较从而导致访问越界。

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

void solve() {
    string s;
    cin>>s;
    int ans=0;
    for (int i = 0; i <s.size() ; ++i) {
        string g=s.substr(i,5);
        if(g=="edgnb")ans++;
    }
    cout<<ans<<endl;

}

signed main() {
    ios::sync_with_stdio(0);
    cin.tie(0);
    int t=1;
//    cin >> t;
    while (t--) {
        solve();
    }
}

F - Encoded Strings I

模拟。

没读,队友写的,看起来直接模拟就行。

点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define int long long
const int mod = 998244353;
#define endl '\n'


void solve() {
    int n;
    cin >> n;
    string g;
    cin >> g;
    string s;
    string res = "";
    s = "";
    for (int i = 0; i < g.size(); ++i) {
//        s += g[i];
        string s=g.substr(0,i+1);
        vector<int>vis(26,-1);
        int dq=-1;
        std::reverse(s.begin(), s.end());
        for(auto &x:s) {
            if (vis[x - 'a'] == -1) {
                vis[x - 'a'] = ++dq;
//                dq++;
                x = 'a' + dq;
            } else {
                x = 'a' + vis[x-'a'];
            }
        }
        std::reverse(s.begin(), s.end());
        res= max(res,s);
    }
    cout << res << endl;
}

signed main() {
    ios::sync_with_stdio(0);
    cin.tie(0);
    int t=1;
//    cin >> t;
    while (t--) {
        solve();
    }

}

H - Line Graph Matching

边双联通分量。

容易发现偶数条边总是可以通过贪心匹配完的,奇数条边会有一条边不能被匹配;再进一步分析,如果奇数条边中删的边是割边,那么分成的两个联通块中边数有奇数的就会缺少匹配,所以要保证割掉这条边后两个联通块都是偶数边才会更优。

具体地,用边双联通分量缩点后,对于每个子树内是偶数边数时,可以删掉割边计算答案;非割边的情况可以预处理即可。

点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define int long long
const int mod = 998244353;
#define endl '\n'
const int N=5e5+3;
#define endl '\n'
int  cnt;
int dfn[N], low[N];
vector<array<int,3>> g[N];
vector<int>ans[N];
stack<int> st;
int dcc;
///c[x]表示x所属的边双联通分量编号
int c[N];
int s = 0;

void tarjan(int x, int las) {
    low[x] = dfn[x] = ++cnt;
    st.push(x);
    for (auto f: g[x]) {
        pair<int,int>i={f[0],f[1]};
        if (i.second == (las ^ 1)) continue;
        if (!dfn[i.first]) {
            tarjan(i.first, i.second);
            low[x] = min(low[x], low[i.first]);
        } else low[x] = min(low[x], dfn[i.first]);
    }
    if (dfn[x] == low[x]) {
        vector<int> vec;
        dcc++;
        ans[dcc].push_back(x);
        c[x]=dcc;
        while (st.top() != x) {
            ans[dcc].push_back(st.top());
            c[st.top()] = dcc;
            st.pop();
        }
        st.pop();
    }
}
void init(int n) {
    cnt = 0;
    dcc = 0;
    while (st.size())st.pop();
    for (int i = 0; i <= n; ++i) {
        g[i].clear();
        ans[i].clear();
        low[i] = dfn[i] = 0;
        c[i] = 0;
    }
}
vector<array<int,2>>mp[N];
int a[N];
int res=0;
int f[N];
void dfs(int u,int fa) {
    f[u] = a[u];
    for (auto [v, w]: mp[u]) {
        if (v == fa)continue;
        dfs(v, u);
        f[u] += f[v]+1;
        if (f[v] % 2 == 0) {
            res = max(res, s - w);
        }
    }
}
void solve() {
    int n, m;
    cin >> n >> m;
    vector<int> du(n + 1);

    vector<array<int, 3>> edge;

    for (int i = 1; i <= m; ++i) {
        int u, v, w;
        cin >> u >> v >> w;
        s += w;
        g[u].push_back({v, i << 1, w});
        g[v].push_back({u, i << 1 | 1, w});
    }
    if (m % 2 == 0) {
        cout << s << endl;
        return;
    }
    for (int i = 1; i <= n; ++i) {
        if (!dfn[i]) tarjan(i, 0);
    }
    for (int i = 1; i <= n; ++i) {
        for (auto [v, id, w]: g[i]) {
            if (c[v] != c[i]) {
                mp[c[i]].push_back({c[v], w});
            }
        }
    }
    for (int i = 1; i <= dcc; ++i) {
        std::sort(mp[i].begin(), mp[i].end());
        mp[i].erase(unique(mp[i].begin(), mp[i].end()), mp[i].end());
    }
    vector<int> vis(n + 1);
    for (int i = 1; i <= n; ++i) {
        if (vis[i] == 0) {
            int sum = 0;
            queue<int> q;
            q.push(i);
            while (q.size()) {
                auto u = q.front();
                q.pop();
                vis[u] = 1;
                for (auto [v, idx, w]: g[u]) {
                    if (c[v] == c[u]) {
                        res= max(res,s-w);
                        sum++;
                        if (vis[v] == 0) {
                            vis[v] = 1;
                            q.push(v);
                        }
                    }
                }
            }
            a[c[i]] = sum;
        }
    }
//    cout<<res<<endl;
//    for (int i = 1; i <= dcc; ++i) {
//        for (auto x: ans[i]) {
//            cout << x << ' ';
//        }
//        cout << endl;
//    }
//    cout << endl;
    for (int i = 1; i <= dcc; ++i) {
        a[i] /= 2;
//        cout << a[i] << ' ';
    }

    dfs(1, 0);
    cout << res << endl;
}

signed main() {
    ios::sync_with_stdio(0);
    cin.tie(0);
    int t=1;
//    cin >> t;
    while (t--) {
        solve();
    }

}

J - Luggage Lock

最短路。

发现给定的两个字符串可能有 \(1\times 10^4\) 种状态,又是多源,复杂度是难以承受的;但是两个不同状态之间有相对关系,将一个状态调整到全 \(0\) 状态后,另外一个状态也调整相应步数,这时候它们的最短路程和未调整前是一样的,即对于 \(u\rightarrow v\) 来说,将 \(u\) 都调整到全 \(0\) 的状态,\(v\) 调整相应的步数,那么在一开始用全 \(0\) 跑完 \(\text{dijkstra}\) 就可以 \(O(1)\) 回答。

点击查看代码
#include <bits/stdc++.h>
#define PII pair<int,int>
#define endl '\n'
using namespace std;

using i64 = long long;
const int N=1e4+10,M=1e5+10;
string a, b;

vector<PII>g[N];
int vis[N],mn[N];
int ans[M];
void solve() {
	int m;
	cin>>a>>b;
	int s=0;
	for(int i=0;i<4;i++){
		int p=(b[i]-a[i]+10)%10;
		s*=10;
		s+=p;
	}
//	cout<<s<<endl;
	cout<<mn[s]<<endl;
}
int main() {
//	freopen("in.txt", "r", stdin);
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	queue<PII>q;

	for(int l=0;l<1;l++){
		int pp=l;
		q.push({0,pp});
		for(int i=0;i<1e4;i++){
			vis[i]=0;
			mn[i]=1e9;
		}
		mn[pp]=0;
		while(q.size()){
			auto [s,x]=q.front();
			q.pop();
			if(vis[x]){
				continue;
			}
			vis[x]=1;
			for (int i = 1; i <= 4; i += 1) {
				for (int j = 0; j + i - 1 < 4; j += 1) {
					int nxt = x;
					for (int k = j; k <= j + i - 1; k += 1) {
						int p=pow(10,3-k);
						if (nxt/p%10 == 9) {
							nxt+=p;
							nxt-=p*10;
						}
						else {
							nxt+=p;
						}
					}
					if(s+1<mn[nxt]){
						mn[nxt]=s+1;
						q.push({s+1,nxt});
					}
					nxt = x;
					for (int k = j; k <= j + i - 1; k += 1) {
						int p=pow(10,3-k);
						if (nxt/p%10 == 0) {
							nxt-=p;
							nxt+=p*10;
						}
						else {
							nxt-=p;
						}
					}
					if(s+1<mn[nxt]){
						mn[nxt]=s+1;
						q.push({s+1,nxt});
					}
				}
			}
		}
		
	}
	int t=1;
	cin >> t;
	while (t--) {
		solve();
	}
	
	return 0;
}

L - Perfect Matchings

容斥,树形 \(dp\)

\(dp_{u,i,0/1}\) 表示第 \(u\) 个点的子树内匹配了 \(i\) 对且 不包含 / 包含 点 \(i\) 的方案数。

定义 \(v\in Sub_u\),那么有转移方程:

\[dp_{u,i+j,0} += dp_{u,i,0}\times (dp_{v,j,0}+dp_{v,j,1})\\ dp_{u,i+j,1} += dp_{u,i,1}\times (dp_{v,j,0}+dp_{v,j,1})\\ dp_{u,i+j+1,1} += dp_{u,i,0}\times dp_{v,j,0} \]

得到 \(dp_{u,i,0/1}\) 之后,我们还要计算剩下 \(n-i\) 对点的匹配数,定义 \(x = n - i\),首先要选出一半的点 \(\binom{2x}{x}\),然后另外一半的点可以任意匹配,有全排列数 \(x!\),每一边还可以交换再算,会算重 \(2^x\) 次,所以另一半的方案数即 \(\frac{\binom{2x}{x}x!}{2^x}\),最后根据容斥原理判断系数为 \((-1)^i\)

代码中 Z 类型为取模类。

参考[1]

点击查看代码
using Z = ModInt<MOD[0]>;

struct Comb {
    int n;
    std::vector<Z> _fac; //阶乘
    std::vector<Z> _invfac; //阶乘的逆元
    std::vector<Z> _inv; //数字的逆元

    Comb() : n{0}, _fac{1}, _invfac{1}, _inv{0} {}
    Comb(int n) : Comb() {
        init(n);
    }

    void init(int m) {
        if (m <= n) return;
        _fac.resize(m + 1);
        _invfac.resize(m + 1);
        _inv.resize(m + 1);

        for (int i = n + 1; i <= m; i++) {
            _fac[i] = _fac[i - 1] * i;
        }
        _invfac[m] = _fac[m].inv();
        for (int i = m; i > n; i--) {
            _invfac[i - 1] = _invfac[i] * i;
            _inv[i] = _invfac[i] * _fac[i - 1];
        }
        n = m;
    }

    Z fac(int m) {
        if (m > n) init(2 * m);
        return _fac[m];
    }
    Z invfac(int m) {
        if (m > n) init(2 * m);
        return _invfac[m];
    }
    Z inv(int m) {
        if (m > n) init(2 * m);
        return _inv[m];
    }
    Z C(int n, int m) {
        if (n < m || m < 0) return 0;
        return fac(n) * invfac(m) * invfac(n - m);
    }
    Z A(int n, int m) {
        if (n < m || m < 0 ) return 0;
        return fac(n) * invfac(n - m);
    }
} comb;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int n;
    cin >> n;

    n <<= 1;

    vector e(n + 1, vector<int>());
    for(int i = 1; i < n; i += 1) {
        int u, v;
        cin >> u >> v;
        e[u].push_back(v);
        e[v].push_back(u);
    }

    vector<int> siz(n + 1);
    vector dp(n + 1, vector<array<Z,2>>(n / 2 + 2));
    vector<array<Z,2>> ndp;
    auto dfs = [&](auto &&self, int u, int fa)->void{
        siz[u] = 1;
        dp[u][0][0] = 1;

        for(auto &v : e[u]) {
            if(v == fa) continue;
            self(self, v, u);

            ndp.assign((siz[u] + siz[v]) / 2 + 2, {0, 0});
            for(int i = siz[u] / 2; i >= 0; i -= 1) {
                for(int j = siz[v] / 2; j >= 0; j -= 1) {
                    ndp[i + j][0] += dp[u][i][0] * (dp[v][j][0] + dp[v][j][1]);
                    ndp[i + j][1] += dp[u][i][1] * (dp[v][j][0] + dp[v][j][1]);
                    ndp[i + j + 1][1] += dp[u][i][0] * dp[v][j][0];
                }
            }
            siz[u] += siz[v];
            for(int i = 0;i <= siz[u] / 2 + 1;i += 1){
                dp[u][i] = ndp[i];
            }
        }
    };
    dfs(dfs, 1, 0);

    Z ans = 0;
    for(int i = 0; i <= n / 2; i += 1) {
        Z w = dp[1][i][0] + dp[1][i][1];
        int x = n / 2 - i;
        w *= comb.C(x * 2, x) * comb.fac(x) / power<Z>(2, x);

        ans += (i & 1 ? -1 : 1) * w;
    }

    cout << ans << "\n";

    return 0;
}

  1. https://www.cnblogs.com/QuantAsk/p/15585607.html ↩︎

posted @ 2025-10-01 22:25  Ke_scholar  阅读(10)  评论(0)    收藏  举报