您的位置:首页 > 其它

【解题报告】ICM Technex 2017 and Codeforces Round #400 (Div. 1 + Div. 2, combined)

2017-03-02 23:16 597 查看
题目链接

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


(其它题目略)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐