【解题报告】ICM Technex 2017 and Codeforces Round #400 (Div. 1 + Div. 2, combined)
2017-03-02 23:16
597 查看
题目链接
其次, k=1,k=−1 是特殊的情况需要特判。
本题有三种解法(以下的三份代码对应着三种解法):
DFS 染色:因为门相当于开关之间的某种关系,因此以开关为点,门为边建图。边的权值为门原本的开闭状态。然后尝试给点染色,原则是相邻的点由边权决定是同色还是异色。用 DFS 边染色边检查当前图的染色是否合法。若存在染色方案则存在解。
并查集合并连通分量:与上个方法相似,同样以开关为点建图。但是要把开关拆点。例如点 x 拆成 x0 和 x1 , 其中 x 表示开关打开, x1 表示开关关闭。根据门的信息将开关相连(例如当点 x 和点 y 表示的开关要么同时打开,要么同时关闭的话,往图中加入边( x0 , y0 )和边( x1 , y1 ))。接着,用并查集合并连通分量。最后对于任意点 x ,若 x0,x1 在同一个连通分量内则不存在解,否则有解。
2−SAT :这种解法相当模板,如果对这种问题及其解法有所了解的话就很容易知道解法是什么,而且在有模板的情况下能够相当快速地实现。这里就不赘述了。
(其它题目略)
A. A Serial Killer(Codeforces 776A)
思路
维护两个字符串,每读取两个字符串就与维护的串作比较,将其中一个替换成另一个即可。代码
#include <bits/stdc++.h> using namespace std; int n; string a, b, x, y; int main() { // freopen("data.txt", "r", stdin); ios::sync_with_stdio(false); cin.tie(0); cin >> a >> b >> n; cout << a << ' ' << b << endl; for(int i = 1; i <= n; i++) { cin >> x >> y; if(a == x) { a = y; } else { b = y; } cout << a << ' ' << b << endl; } return 0; }
B. Sherlock and his girlfriend(Codeforces 776B)
思路
刚看到题目的想法是,模仿素数筛的方法,每扫描到一个素数就将其倍数涂成不同的颜色。后来发现素数涂一种颜色,合数涂另一种颜色即可。那么就是用 埃拉伯色尼筛法 算出哪些是素数就好了。注意特判 n=1,n=2 的情况。代码
#include <bits/stdc++.h> using namespace std; const int maxn = 1e5 + 10; bool isPrime[maxn]; int n; int main() { // freopen("data.txt", "r", stdin); ios::sync_with_stdio(false); cin.tie(0); cin >> n; if(n == 1) { cout << 1 << endl << 1 << endl; return 0; } if(n == 2) { cout << 1 << endl << 1 << ' ' << 1 << endl; return 0; } fill(isPrime + 2, isPrime + n + 2, true); for(int i = 2; i <= n + 1; i++) { if(isPrime[i] == false) { continue; } for(int j = i + i; j <= n + 1; j += i) { isPrime[j] = false; } } cout << 2 << endl; for(int i = 2; i <= n + 1; i++) { if(isPrime[i] == true) { cout << 1 << ' '; } else { cout << 2 << ' '; } } return 0; }
C. Molly’s Chemicals(Codeforces 776C)
思路
首先,因为是求子段和的问题,所以先处理出前缀和,也就是 sum[i] 代表数组元素 a[1..i] 的和。然后可以枚举区间的一个端点 r ,然后枚举 k 的幂 p 。我们需要快速知道是否存在对应的 l 使得 a[l]+kp=a[r],以便确认区间 (l,r] 是否是解。因此我们维护一个映射( Map ) M , M[i] 表示我们遍历过了的数组元素(前缀和)中有多少个 i 。然后就能知道对于枚举到的 r 是否存在 l ,以及存在多少 l 。其次, k=1,k=−1 是特殊的情况需要特判。
代码
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 1e5 + 5; const ll INF = (1LL << 50); map <ll, ll> M; ll d, ans = 0, a[maxn], Pow[100]; int main() { // freopen("data.txt", "r", stdin); int n, k; scanf("%d%d", &n, &k); for(int i = 1; i <= n; i++) { scanf("%I64d", &a[i]); // 处理前缀和 a[i] += a[i-1]; } // 特判 if(k == 1) { M[0] = 1; for(int i = 1; i <= n; i++) { d = a[i] - 1; if(M[d] > 0) { ans += M[d]; } M[a[i]] ++; } printf("%I64d\n", ans); return 0; } // 特判 if(k == -1) { M[0] = 1; for(int i = 1; i <= n; i++) { d = a[i] - 1; if(M[d] > 0) { ans += M[d]; } d = a[i] + 1; if(M[d] > 0) { ans += M[d]; } M[a[i]] ++; } printf("%I64d\n", ans); return 0; } // 预处理k的幂次 Pow[0] = 1; for(int i = 1; abs(Pow[i-1] * k) < INF; i++) { Pow[i] = Pow[i-1] * k; } M[0] = 1; // 枚举r for(int i = 1; i <= n; i++) { // 枚举k的幂次 for(int j = 0; abs(Pow[j-1] * k) < INF; j++) { d = a[i] - Pow[j]; // 若存在l if(M.count(d)) { ans += M[d]; } } // 维护M if(M.count(a[i])) { M[a[i]] ++; } else { M[a[i]] = 1; } } printf("%I64d\n", ans); return 0; }
D. The Door Problem(Codeforces 776D)
思路
本题的突破口是,每个门只对应两个开关。那么对于一个门来说,若它本来是开的,那么对应于它的两个开关要么同时按下,要么同时不按下(才能保持状态)。若它本来是关的,那么对应于它的开关必须一个按下而另一个不按下(才能打开)。本题有三种解法(以下的三份代码对应着三种解法):
DFS 染色:因为门相当于开关之间的某种关系,因此以开关为点,门为边建图。边的权值为门原本的开闭状态。然后尝试给点染色,原则是相邻的点由边权决定是同色还是异色。用 DFS 边染色边检查当前图的染色是否合法。若存在染色方案则存在解。
并查集合并连通分量:与上个方法相似,同样以开关为点建图。但是要把开关拆点。例如点 x 拆成 x0 和 x1 , 其中 x 表示开关打开, x1 表示开关关闭。根据门的信息将开关相连(例如当点 x 和点 y 表示的开关要么同时打开,要么同时关闭的话,往图中加入边( x0 , y0 )和边( x1 , y1 ))。接着,用并查集合并连通分量。最后对于任意点 x ,若 x0,x1 在同一个连通分量内则不存在解,否则有解。
2−SAT :这种解法相当模板,如果对这种问题及其解法有所了解的话就很容易知道解法是什么,而且在有模板的情况下能够相当快速地实现。这里就不赘述了。
代码1
#include <bits/stdc++.h> using namespace std; const int maxn = 1e5 + 10; vector <int> G[maxn]; bool vis[maxn], color[maxn], s[maxn]; int n, m, num, u, edge[maxn]; // 返回是否染色成功 bool dfs(int u) { vis[u] = true; // 访问每条边 for(int i = 0; i < G[u].size(); i++) { int e = G[u][i]; int v = edge[e] ^ u; // 点v应该被染成颜色c int c = s[e] ^ color[u]; if(vis[v] == true) { if(color[v] != c) { return false; } continue; } color[v] = c; if(dfs(v) == false) { return false; } } return true; } int main() { // freopen("data.txt", "r", stdin); scanf("%d%d", &n, &m); for(int i = 1; i <= n; i++) { scanf("%d", &s[i]); s[i] ^= 1; } for(int i = 1; i <= m; i++) { scanf("%d", &num); while(num--) { scanf("%d", &u); G[i].push_back(u); edge[u] ^= i; } } for(int i = 1; i <= m; i++) { if(!vis[i] && !dfs(i)) { puts("NO"); return 0; } } puts("YES"); return 0; }
代码2
#include <bits/stdc++.h> using namespace std; const int maxn = 1e5 + 5; vector <int> G[maxn]; bool same, diff, s[maxn]; int n, m, u, v, num; // 并查集 struct DisjointSet { int p[maxn<<1]; void Init(int n) { for(int i = 1; i <= n; i++) { p[i] = i; } } int Find(int x) { return p[x] == x ? x : p[x] = Find(p[x]); } void Union(int x, int y) { x = Find(x); y = Find(y); p[x] = y; } }o; int main() { // freopen("data.txt", "r", stdin); scanf("%d%d", &n, &m); for(int i = 1; i <= n; i++) { scanf("%d", &s[i]); } for(int i = 1; i <= m; i++) { scanf("%d", &num); while(num--) { scanf("%d", &u); G[u].push_back(i); } } o.Init(2 * m); // 枚举边同时合并连通分量 for(int i = 1; i <= n; i++) { u = G[i][0]; v = G[i][1]; same = s[i]; diff = s[i] ^ 1; o.Union(u, v + diff * m); // u和u + m表示上述u_0和u_1 o.Union(u + m, v + same * m); } for(int i = 1; i <= m; i++) { if(o.Find(i) == o.Find(m + i)) { puts("NO"); return 0; } } puts("YES"); return 0; }
代码3
#include <bits/stdc++.h> using namespace std; const int maxn = 2e5 + 5, maxv = maxn << 1; int n, m, num, u, v, r[maxn]; vector <int> d[maxn]; // 变量的编号范围为[0, N) // 点的编号范围为[0, V) struct TwoSAT { int N, V; vector <int> G[maxv]; vector <int> rG[maxv]; // 后序遍历的顶点列表 vector <int> vs; // 访问标记 bool used[maxv]; // 所属强连通分量的拓扑序 int cmp[maxv]; void init(int n) { N = n; V = 2 * n; } // add AND to CNF void addEdge(int from, int to) { G[from].push_back(to); rG[to].push_back(from); } void dfs(int v) { used[v] = true; for(int i = 0; i < G[v].size(); i++) { if(!used[G[v][i]]) { dfs(G[v][i]); } } vs.push_back(v); } void rdfs(int v, int k) { used[v] = true; cmp[v] = k; for(int i = 0; i < rG[v].size(); i++) { if(!used[rG[v][i]]) { rdfs(rG[v][i], k); } } } int scc() { memset(used, 0, sizeof(used)); vs.clear(); for(int v = 0; v < V; v++) { if(!used[v]) { dfs(v); } } memset(used, 0, sizeof(used)); int k = 0; for(int i = vs.size() - 1; i >= 0; i--) { if(!used[vs[i]]) { rdfs(vs[i], k++); } } return k; } int NOT(int a) { return (a + N) % V; } // (a | b) <=> (!a => b & !b => a) void disjunc(int a, int b) { addEdge(NOT(a), b); addEdge(NOT(b), a); } // (a xor b) <=> (a | b) & (!a | !b) void XOR(int a, int b) { disjunc(a, b); disjunc(NOT(a), NOT(b)); } // !(a xor b) <=> (!a | b) & (a & !b) void notXOR(int a, int b) { disjunc(NOT(a), b); disjunc(a, NOT(b)); } void solve() { scc(); // 判断x和!x是否在不同的强连通分量中 for(int i = 0; i < N; i++) { if(cmp[i] == cmp[N + i]) { puts("NO"); return; } } puts("YES"); // print(); } void print() { // 如果有解,则给出一组解 for(int i = 0; i < N; i++) { if(cmp[i] > cmp[N + i]) { puts("true"); } else { puts("false"); } } } }o; int main() { // freopen("Input3.txt", "r", stdin); scanf("%d%d", &n, &m); for(int i = 0; i < n; i++) { scanf("%d", &r[i]); } for(int i = 0; i < m; i++) { scanf("%d", &num); while(num--) { scanf("%d", &u); d[u - 1].push_back(i); } } o.init(m); for(int i = 0; i < n; i++) { u = d[i][0]; v = d[i][1]; if(r[i] == 0) { o.XOR(u, v); } else { o.notXOR(u, v); } } o.solve(); return 0; }
(其它题目略)
相关文章推荐
- ICM Technex 2017 and Codeforces Round #400 (Div. 1 + Div. 2, combined) 解题报告
- ICM Technex 2017 and Codeforces Round #400 (Div. 1 + Div. 2, combined) C. Molly's Chemicals
- ICM Technex 2017 and Codeforces Round #400 (Div. 1 + Div. 2, combined)
- ICM Technex 2017 and Codeforces Round #400 (Div. 1 + Div. 2, combined)
- ICM Technex 2017 and Codeforces Round #400 (Div. 1 + Div. 2, combined)A. A Serial Killer(水题)
- 【枚举】【前缀和】【map】ICM Technex 2017 and Codeforces Round #400 (Div. 1 + Div. 2, combined) C. Molly's Chemicals
- 【2-SAT】【并查集】ICM Technex 2017 and Codeforces Round #400 (Div. 1 + Div. 2, combined) D. The Door Problem
- ICM Technex 2017 and Codeforces Round #400 (Div. 1 + Div. 2, combined)
- ICM Technex 2017 and Codeforces Round #400 (Div. 1 + Div. 2, combined) 题解(A-E)
- [Updating]ICM Technex 2017 and Codeforces Round #400 (Div. 1 + Div. 2, combined)
- 【前缀和 && 思维转换】ICM Technex 2017 and Codeforces Round #400 (Div. 1 + Div. 2, combined)Molly's Chemicals
- ICM Technex 2017 and Codeforces Round #400 (Div. 1 + Div. 2, combined) C. Molly's Chemicals
- ICM Technex 2017 and Codeforces Round #400 (Div. 1 + Div. 2, combined) A
- ICM Technex 2017 and Codeforces Round #400 (Div. 1 + Div. 2, combined)
- ICM Technex 2017 and Codeforces Round #400 (Div. 1 + Div. 2, combined) A map B贪心 C思路前缀
- ICM Technex 2017 and Codeforces Round #400 (Div. 1 + Div. 2, combined) C
- ICM Technex 2017 and Codeforces Round #400 (Div. 1 + Div. 2, combined) E. The Holmes Children
- ICM Technex 2017 and Codeforces Round #400 (Div. 1 + Div. 2, combined)A+B
- ICM Technex 2017 and Codeforces Round #400 (Div. 1 + Div. 2, combined) D
- ICM Technex 2017 and Codeforces Round #400 (Div. 1 + Div. 2, combined) C. Molly's Chemicals