2023ICPC网络赛第一场vp + 补题
solved 5 problems
L、D、G:@dbywsc
A、 I:@xmjlove
L. KaChang!
思路
签到题,注意一下 \(k > 2\) 就行。
代码
void solve(void) {
int n, t; std::cin >> n >> t;
std::vector<int> a(n);
for(int i = 0; i < n; i++) std::cin >> a[i];
int maxn = *std::max_element(a.begin(), a.end());
std::cout << std::max(2, (int)std::ceil(maxn * 1.0 / t));
}
D.Transitivity
思路
分情况讨论,如果整张图只有一个连通块,那么扩充到完全图即可;如果整张图有多个连通块,且这些连通块内部不传递,那么分别给它们扩充到完全图;如果整张图是多个内部传递的连通块,就选两个最小的连通块连起来,此时需要两两连边。
维护的过程用并查集。
代码
struct DSU {
std::vector<int> p, siz;
DSU(int n): p(n + 1), siz(n + 1, 1) {
std::iota(p.begin(), p.end(), 0);
}
int find(int x) {
if(x == p[x]) return p[x];
return p[x] = find(p[x]);
}
void unite(int a, int b) {
int pa = find(a), pb = find(b);
if(pa == pb) return;
if(siz[pa] < siz[pb]) std::swap(pa, pb);
p[pb] = pa;
siz[pa] += siz[pb];
}
bool same(int u, int v) {return find(u) == find(v);}
int size(int u) {return siz[find(u)];}
};
void solve(void) {
int n, m; std::cin >> n >> m;
std::vector<PII> a(m);
DSU dsu(n + 1);
std::vector<int> ecnt(n + 1);
for(int i = 0; i < m; i++) std::cin >> a[i].x >> a[i].y;
for(int i = 0; i < m; i++) dsu.unite(a[i].x, a[i].y);
for(int i = 0; i < m; i++) {
ecnt[dsu.find(a[i].x)]++;
}
std::vector<PII> comps;
for(int i = 1; i <= n; i++) {
if(dsu.find(i) == i) {
comps.emplace_back(dsu.size(i), ecnt[i]);
}
}
i64 mm = 0;
for(auto [i, j] : comps) {
mm += (i * (i - 1) / 2) - j;
}
if(comps.size() == 1) {
std::cout << mm << '\n';
} else {
if(mm > 0) std::cout << mm << '\n';
else {
std::sort(comps.begin(), comps.end());
std::cout << comps[0].x * comps[1].x;
}
}
}
G.Spanning Tree
思路
分子显然只有一种,所以只需要计算分母有多少种情况就行。
维护两个并查集,第一个并查集模拟加边的过程,加边过程中计算分母。第二个并查集验证给定的生成树能否通过上面的操作生成。如果验证不通过答案就为 \(0\) ,否则输出分数即可。
代码
struct DSU {
std::vector<int> p, siz;
DSU(int n): p(n + 1), siz(n + 1, 1) {
std::iota(p.begin(), p.end(), 0);
}
int find(int x) {
if(x == p[x]) return p[x];
return p[x] = find(p[x]);
}
void unite(int a, int b) {
int pa = find(a), pb = find(b);
if(pa == pb) return;
if(siz[pa] < siz[pb]) std::swap(pa, pb);
p[pb] = pa;
siz[pa] += siz[pb];
}
bool same(int u, int v) {return find(u) == find(v);}
int size(int u) {return siz[find(u)];}
};
std::vector<int> G[N];
i64 qpow(i64 a, i64 b = P - 2) {
i64 res = 1;
while(b) {
if(b & 1) res = res * a % P;
a = a * a % P;
b >>= 1;
}
return res;
}
void solve(void) {
int n; std::cin >> n;
if(n == 1) {
std::cout << "1\n";
return;
}
std::vector<PII> a(n);
for(int i = 0; i < n - 1; i++) std::cin >> a[i].x >> a[i].y;
for(int i = 0; i < n - 1; i++) {
int u, v; std::cin >> u >> v;
G[u].push_back(v);
G[v].push_back(u);
}
DSU dsu(n);
i64 ans = 1;
for(int i = 0; i < n - 1; i++) {
int x = a[i].x, y = a[i].y;
ans = ans * dsu.size(x) % P * dsu.size(y) % P;
dsu.unite(x, y);
}
DSU dsu2(n);
for(int i = 0; i < n - 1; i++) {
int x = a[i].x, y = a[i].y;
if(dsu2.same(x, y)) {
std::cout << "0\n";
return;
}
int px = dsu2.find(x), py = dsu2.find(y);
if(G[px].size() < G[py].size()) std::swap(px, py);
int cnt = 0;
for(auto v : G[py]) {
if(dsu2.find(v) == px) {
cnt++;
if(cnt > 1) break;
}
}
if(cnt != 1) {
std::cout << "0\n";
return;
}
//dsu2.unite(px, py);
dsu2.p[py] = px;
dsu2.siz[px] += dsu2.siz[py];
for(auto v : G[py]) {
int pv = dsu2.find(v);
if(pv != px) {
G[px].push_back(pv);
}
}
std::vector<int>().swap(G[py]);
}
std::cout << qpow(ans) << '\n';
}
A.Qualifiers Ranking Rules
思路
签到题,两场读取的时候分别进行排名和去重,最后再合并加去重即可
代码
String[] input = br.readLine().split(" ");
int n = Integer.parseInt(input[0]);
int m = Integer.parseInt(input[1]);
HashMap<Integer, String> dict1 = new HashMap<>();
HashSet<String> set1 = new HashSet<>();
int cnt = 1;
for (int i = 0; i < n; i++) {
String school = br.readLine();
if (!set1.contains(school)) {
dict1.put(cnt++, school);
set1.add(school);
}
}
cnt = 1;
HashMap<Integer, String> dict2 = new HashMap<>();
HashSet<String> set2 = new HashSet<>();
for (int i = 0; i < m; i++) {
String school = br.readLine();
if (!set2.contains(school)) {
dict2.put(cnt++, school);
set2.add(school);
}
}
HashMap<Integer, String> ans = new HashMap<>();
HashSet<String> set3 = new HashSet<>();
cnt = 1;
for (int i = 1; i <= Math.max(n, m); i++) {
if (dict1.get(i) != null) {
if (!set3.contains(dict1.get(i))) {
ans.put(cnt++, dict1.get(i));
set3.add(dict1.get(i));
}
}
if (dict2.get(i) != null) {
if (!set3.contains(dict2.get(i))) {
ans.put(cnt++, dict2.get(i));
set3.add(dict2.get(i));
}
}
}
for (int i = 1; i <= set3.size(); i++) {
pw.println(ans.get(i));
}
I. Pa?sWorD
思路
设 \(dp [i] [j] [k]\) 表示来到密码的第一个位置,当前字符为 \(j\) ,对于三个条件的满足状况为 \(k\) 的情况数
\(dp [i] [j] [k]\) 可以从 \(dp [i - 1] [x] [k]\) 和 \(dp [i - 1] [x] [k ^ (1 << i)]\) 转移过来,\(x\) 表示不为 \(j\) 的所有字符,\(i\) 表示当前的字符可以帮助满足哪个条件
即一个状态可以通过上一个位置 ,字符不等于当前字符,状态等于当前状态,或者等于当前状态取消掉本位置字符的影响后的状态转移过来
时间复杂度为 \(O(n \times 字符数量 ^ 2 \times 状态数)\) ,字符数量为 \(62\) ,状态数为 \(7\) ,所以为 \(O(n \times 3844 \times 7)\)
其中转移的代价为 \(O(62)\) ,我们可以预处理好上一个位置上所有状态的情况数和,这样代价就变成了 \(O(1)\)
代码
int n = Integer.parseInt(br.readLine());
int MOD = 998244353;
char[] s = br.readLine().toCharArray();
HashMap<Character, Integer> dict = new HashMap<>();
for (char i = '0'; i <= '9'; i++) {
dict.put(i, i - '0');
}
for (char i = 'a'; i <= 'z'; i++) {
dict.put(i, i - 'a' + 10);
}
for (char i = 'A'; i <= 'Z'; i++) {
dict.put(i, i - 'A' + 36);
}
int[][] dp = new int[62][1 << 4];
int[] sum2 = new int[1 << 4];
int[][] sum3 = new int[62][1 << 4];
sum2[0] = 1;
for (int i = 0; i < n; i++) {
int[] nsum2 = new int[1 << 4];
int[][] nsum3 = new int[62][1 << 3];
for (int j = 0; j < 62; j++) {
if (s[i] >= 'A' && s[i] <= 'Z' && j != dict.get(s[i]))
continue;
if (s[i] >= '0' && s[i] <= '9' && j != dict.get(s[i]))
continue;
if (s[i] >= 'a' && s[i] <= 'z' && j != dict.get(s[i]) && j != dict.get(s[i]) + 26)
continue;
for (int k = (1 << 3) - 1; k >= 0; k--) {
if (j >= 36 && (k & 1) == 0) {
continue;
} else if (j >= 10 && j < 36 && (k & (1 << 1)) == 0) {
continue;
} else if (j < 10 && (k & (1 << 2)) == 0) {
continue;
}
if (j >= 36) {
dp[j][k] = (sum2[k] + sum2[k ^ 1]) % MOD;
dp[j][k] = (dp[j][k] - sum3[j][k] + MOD) % MOD;
dp[j][k] = (dp[j][k] - sum3[j][k ^ 1] + MOD) % MOD;
} else if (j >= 10) {
dp[j][k] = (sum2[k] + sum2[k ^ (1 << 1)]) % MOD;
dp[j][k] = (dp[j][k] - sum3[j][k] + MOD) % MOD;
dp[j][k] = (dp[j][k] - sum3[j][k ^ (1 << 1)] + MOD) % MOD;
} else {
dp[j][k] = (sum2[k] + sum2[k ^ (1 << 2)]) % MOD;
dp[j][k] = (dp[j][k] - sum3[j][k] + MOD) % MOD;
dp[j][k] = (dp[j][k] - sum3[j][k ^ (1 << 2)] + MOD) % MOD;
}
nsum2[k] += dp[j][k];
nsum2[k] %= MOD;
nsum3[j][k] = dp[j][k];
}
}
sum2 = Arrays.copyOf(nsum2, 1 << 4);
for (int j = 0; j < 62; j++) {
sum3[j] = Arrays.copyOf(nsum3[j], 1 << 4);
}
}
pw.println(sum2[(1 << 3) - 1]);
pw.flush();

浙公网安备 33010602011771号