A. Arpa’s hard exam and Mehrdad’s naive cheat(Codeforces 742A)
思路
於是將
代碼
#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)
思路
看到題目很自然地想枚舉二元組
在數據中尋找突破點。我們發現
代碼
#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)
思路
這題看上去貌似不太容易發現端倪,但是如果注意到
根據題目給出的信息,有向圖中的每個節點的出度爲
然後就是處理奇怪的規則:對於任意的
- 先考慮只有一個環的情況(
n′ 表示該環上的點的個數,t′ 表示該環滿足規則需要的最小t )。當環上有奇數個點時,x=y 必須成立才能滿足規則,也就是說對這個環,只有t′=n′ 才能滿足規則。當環上有偶數個點時當然可以模仿奇數情況,但是t′=n′2 顯然可以得到更小的解(表示x,y 互爲“在對面的點”)。 - 其次考慮有多個環的情況(只可能在不同的來連通分量上)。當
t 爲所有環的t′ 的倍數時,所有環上的規則仍能運行良好。於是問題就轉化成求所有環的t′ 的最小公倍數。 - 最後考慮有點不出現在環上的情況。對在環上的點計數,若計數結果不等於
n 的話這種情況就發生了(點的出度爲1 保證了計數不會重複)。
根據以上分析,只要找到圖中所有的環的長度,問題就迎刃而解了。那麼我們可以對圖用類似拓撲排序的搜索(這裏是DFS)的方法找環(將未訪問,訪問過,正在訪問的點分別標記爲
代碼
#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;
}
(其它題目略)