A. Arpa’s hard exam and Mehrdad’s naive cheat(Codeforces 742A)
思路
1378n
看似很可怕,但实际上如果只求其个位数的话,问题就可以转化成求
8n
的个位数。在纸上用小规模的
n
演算
于是将
n
以
代码
#include <bits/stdc++.h>
using namespace std;
int n;
int main() {
// freopen("data.txt", "r", stdin);
scanf("%d", &n);
if(n == 0) {
puts("1");
}
else if(n % 4 == 1) {
puts("8");
}
else if(n % 4 == 2) {
puts("4");
}
else if(n % 4 == 3) {
puts("2");
}
else {
puts("6");
}
return 0;
}
B. Arpa’s obvious problem and Mehrdad’s terrible solution(Codeforces 742B)
思路
看到题目很自然地想枚举二元组
(i,j)
去验证是否有
a[i]⊕a[j]=x
。但显然
105
这种数据规模是不允许
O(n2)
的算法出现的。
在数据中寻找突破点。我们发现
a[i]
的值的上限是
105
,这就告诉我们,可以用数组开
105
的空间(这里用
cnt
数组)来记录每个数值出现次数。这样我们只用枚举
j
,访问过的
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 10;
int n, x, a[maxn], cnt[maxn<<2];
ll ans;
int main() {
// freopen("data.txt", "r", stdin);
scanf("%d%d", &n, &x);
for(int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
}
for(int i = 1; i <= n; i++) {
ans += cnt[a[i]^x];
cnt[a[i]]++;
}
printf("%I64d\n", ans);
return 0;
}
C. Arpa’s loud Owf and Mehrdad’s evil plan(Codeforces 742C)
思路
这题看上去貌似不太容易发现端倪,但是如果注意到
i
与
根据题目给出的信息,有向图中的每个节点的出度为
1
(这个很重要,避免了很多奇怪的环图的出现)。
然后就是处理奇怪的规则:对于任意的
- 先考虑只有一个环的情况(
n′
表示该环上的点的个数,
t′
表示该环满足规则需要的最小
t
)。当环上有奇数个点时,
x=y 必须成立才能满足规则,也就是说对这个环,只有 t′=n′ 才能满足规则。当环上有偶数个点时当然可以模仿奇数情况,但是 t′=n′2 显然可以得到更小的解(表示 x,y 互为“在对面的点”)。 - 其次考虑有多个环的情况(只可能在不同的来连通分量上)。当
t
为所有环的
t′ 的倍数时,所有环上的规则仍能运行良好。于是问题就转化成求所有环的 t′ 的最小公倍数。 - 最后考虑有点不出现在环上的情况。对在环上的点计数,若计数结果不等于
n
的话这种情况就发生了(点的出度为
1 保证了计数不会重复)。
根据以上分析,只要找到图中所有的环的长度,问题就迎刃而解了。那么我们可以对图用类似拓扑排序的搜索(这里是DFS)的方法找环(将未访问,访问过,正在访问的点分别标记为 −1,1,0 ,当访问标记为 0 的点时就说明遇到了环)。对每个节点记录下该点在搜索树中的深度,就能在碰到环的时候计算出环的长度了。
代码
#include <bits/stdc++.h>
using namespace std;
const int maxn = 110;
bool fail;
// vis保存了{-1, 0, 1}三种值
int n, a, ans, sum, vis[maxn], dep[maxn];
vector <int> vec, G[maxn];
// 找到所有的环并计算其长度
void dfs(int u, int d) {
vis[u] = 0;
dep[u] = d;
for(int v : G[u]) {
// 发现环的存在
if(vis[v] == 0) {
// 计算长度并保存下来
vec.push_back(d - dep[v] + 1);
// 用于判断是否有点不在环上
sum += d - dep[v] + 1;
}
// 继续访问下个节点
else if(vis[v] < 0) {
dfs(v, d + 1);
}
// 用于判断是否有点不在环上(保险措施)
else {
fail = true;
}
}
vis[u] = 1;
}
// 计算最小公倍数
int lcm(int x, int y) {
return x / __gcd(x, y) * y;
}
int main() {
// freopen("data.txt", "r", stdin);
scanf("%d", &n);
for(int i = 1; i <= n; i++) {
scanf("%d", &a);
G[i].push_back(a);
}
// 访问每个环
memset(vis, -1, sizeof(vis));
for(int i = 1; i <= n; i++) {
if(vis[i] < 0) {
dfs(i, 1);
}
}
if(fail == true || sum != n) {
puts("-1");
return 0;
}
ans = 1;
// 计算最终结果
for(int i = 0; i < vec.size(); i++) {
if(vec[i] % 2 == 0) {
ans = lcm(ans, vec[i] >> 1);
}
else {
ans = lcm(ans, vec[i]);
}
}
printf("%d\n", ans);
return 0;
}
D. Arpa’s weak amphitheater and Mehrdad’s valuable Hoses(Codeforces 742D)
思路
如果熟悉背包问题的人可能比较容易反应过来这是个背包问题的变形。但是即使熟悉背包问题,也会觉得“小团体”要么全拿要么只能拿一个的规则十分棘手。
考虑将有
处理出这些集合又该怎么办呢?熟悉分组背包的人会发现这是一个分组背包问题。每个集合是分组背包中的一个物品组,用一组的物品反复利用上一组的信息更新
代码
#include <bits/stdc++.h>
using namespace std;
const int maxn = 3000;
bool vis[maxn];
int n, m, k, f, u, v, cnt, ans;
int b[maxn], w[maxn], B[maxn], W[maxn], p[maxn], d1[maxn], d2[maxn];
vector <int> G[maxn];
// 并查集的初始化
void init() {
for(int i = 1; i <= n; i++) {
W[i] = w[i];
B[i] = b[i];
p[i] = i;
}
}
// 并查集的查找
int find(int x) {
return x == p[x] ? x : p[x] = find(p[x]);
}
// 并查集的合并
void Union(int x, int y) {
x = find(x);
y = find(y);
if(x == y) {
return;
}
// 数据的合并
W[x] += W[y];
B[x] += B[y];
// 点编号的合并
p[y] = x;
}
int main() {
// freopen("data.txt", "r", stdin);
scanf("%d%d%d", &n, &m, &k);
for(int i = 1; i <= n; i++) {
scanf("%d", &w[i]);
}
for(int i = 1; i <= n; i++) {
scanf("%d", &b[i]);
}
// 处理出“联合实体”
init();
while(m--) {
scanf("%d%d", &u, &v);
Union(u, v);
}
for(int i = 1; i <= n; i++) {
f = find(i);
G[f].push_back(i);
}
// 将“联合实体”放入集合中
cnt = n;
for(int i = 1; i <= n; i++) {
f = find(i);
if(vis[f] == false) {
G[f].push_back(++cnt);
w[cnt] = W[f];
b[cnt] = B[f];
vis[f] = true;
}
}
// 分组背包(滚动数组实现)
memset(d1, 0, sizeof(d1));
for(int i = 1; i <= n; i++) {
memset(d2, 0, sizeof(d2));
for(int u : G[i]) {
for(int j = k; j >= w[u]; j--) {
d2[j] = max(d2[j], d1[j-w[u]] + b[u]);
}
}
for(int j = 0; j <= k; j++) {
d1[j] = max(d1[j], d2[j]);
}
}
// 在DP表中寻找答案
for(int j = 1; j <= k; j++) {
ans = max(ans, d1[j]);
}
printf("%d\n", ans);
return 0;
}
(其它题目略)