2018-2019 ACM-ICPC Pacific Northwest Regional Contest (Div. 1) Solution
A:Exam
Solved.
温暖的签。
1 #include<bits/stdc++.h> 2 3 using namespace std; 4 5 const int maxn = 1e3 + 10; 6 7 int k; 8 char str1[maxn], str2[maxn]; 9 10 int main() 11 { 12 while(~scanf("%d",&k)) 13 { 14 scanf("%s", str1 + 1); 15 scanf("%s", str2 + 1); 16 int cnt1 = 0, cnt2 = 0; 17 int len = strlen(str1 + 1); 18 for(int i = 1; i <= len; ++i) 19 { 20 if(str1[i] == str2[i]) cnt1++; 21 else cnt2++; 22 } 23 int ans = 0; 24 if(cnt1 >= k) 25 { 26 ans = k + cnt2; 27 } 28 else 29 { 30 ans = len - (k - cnt1); 31 } 32 printf("%d\n", ans); 33 } 34 return 0; 35 }
B:Coprime Integers
Solved.
枚举gcd反演
1 #include<bits/stdc++.h> 2 3 using namespace std; 4 5 typedef long long ll; 6 7 const int maxn = 1e7 + 10; 8 9 bool check[maxn]; 10 int prime[maxn]; 11 ll mu[maxn]; 12 13 void Moblus() 14 { 15 memset(check, false, sizeof check); 16 mu[1] = 1; 17 int tot = 0; 18 for(int i = 2; i < maxn; ++i) 19 { 20 if(!check[i]) 21 { 22 prime[tot++] = i; 23 mu[i] = -1; 24 } 25 for(int j = 0; j < tot; ++j) 26 { 27 if(i * prime[j] > maxn) break; 28 check[i * prime[j]] = true; 29 if(i % prime[j] == 0) 30 { 31 mu[i * prime[j]] = 0; 32 break; 33 } 34 else 35 { 36 mu[i * prime[j]] = -mu[i]; 37 } 38 } 39 } 40 } 41 42 ll sum[maxn]; 43 44 ll calc(int n, int m) 45 { 46 ll ans = 0; 47 if(n > m) swap(n, m); 48 for(int i = 1, la = 0; i <= n; i = la + 1) 49 { 50 la = min(n / (n / i), m / (m / i)); 51 ans += (ll)(sum[la] - sum[i - 1]) * (n / i) * (m / i); 52 } 53 return ans; 54 } 55 56 ll a, b, c, d; 57 58 int main() 59 { 60 Moblus(); 61 for(int i = 1; i < maxn; ++i) sum[i] = sum[i - 1] + mu[i]; 62 while(~scanf("%lld %lld %lld %lld", &a, &b, &c, &d)) 63 { 64 ll ans = calc(b, d) - calc(a - 1, d) - calc(b, c - 1) + calc(a - 1, c - 1); 65 printf("%lld\n", ans); 66 } 67 return 0; 68 }
C:Contest Setting
Solved.
题意:
有n个题目,每个题目的难度值不同,要选出k个组成一套$Contest$
要求每个题目难度不同,求方案数
思路:
$把难度值相同的题目放在一起作为一种 dp[i][j] 表示选到第i种题目,已经选了k种的方案数$
$然后做01背包即可,注意加上的值为cnt[i] 表示该种题目有多少个$
1 #include<bits/stdc++.h> 2 3 using namespace std; 4 5 typedef long long ll; 6 7 const ll MOD = 998244353; 8 const int maxn = 1e3 + 10; 9 10 int n, k, pos; 11 ll cnt[maxn]; 12 ll dp[maxn]; 13 map<int, int>mp; 14 15 int main() 16 { 17 while(~scanf("%d %d", &n, &k)) 18 { 19 mp.clear(); 20 pos = 0; 21 memset(cnt, 0, sizeof cnt); 22 for(int i = 1; i <= n; ++i) 23 { 24 int num; 25 scanf("%d", &num); 26 if(mp[num] == 0) mp[num] = ++pos; 27 int id = mp[num]; 28 cnt[id]++; 29 } 30 memset(dp, 0, sizeof dp); 31 dp[0] = 1; 32 for(int i = 1; i <= pos; ++i) 33 { 34 for(int j = k; j >= 1; --j) 35 { 36 dp[j] = (dp[j] + dp[j - 1] * cnt[i] % MOD) % MOD; 37 } 38 } 39 printf("%lld\n", dp[k]); 40 } 41 return 0; 42 }
D:Count The Bits
Solved.
题意:
在$[0, 2^b - 1] 中所有k的倍数以二进制形式表示有多少个1$
思路:
$dp[i][j] 表示枚举二进制位数,j 模k的余数, 表示的是前i位中模k的余数为j的数中1的个数$
$cnt[i][j] 表示当前二进制位为第i位, 模k的余数为j的数的个数$
直接转移即可。
1 #include<bits/stdc++.h> 2 3 using namespace std; 4 5 typedef long long ll; 6 7 const ll MOD = (ll)1e9 + 9; 8 const int maxn = 1e3 + 10; 9 10 int k, b; 11 ll cnt[maxn][maxn]; 12 ll dp[maxn][maxn]; 13 14 int main() 15 { 16 while(~scanf("%d %d", &k, &b)) 17 { 18 memset(cnt, 0, sizeof cnt); 19 memset(dp, 0, sizeof dp); 20 cnt[1][0]++; 21 cnt[1][1 % k]++; 22 dp[1][1 % k]++; 23 ll tmp = 1; 24 for(int i = 1; i <= b; ++i) 25 { 26 tmp = (tmp << 1) % k; 27 for(int j = 0; j < k; ++j) 28 { 29 //0 30 cnt[i + 1][j] = (cnt[i + 1][j] + cnt[i][j]) % MOD; 31 dp[i + 1][j] = (dp[i + 1][j] + dp[i][j]) % MOD; 32 //1 33 ll tmp2 = (tmp + j) % k; 34 cnt[i + 1][tmp2] = (cnt[i + 1][tmp2] + cnt[i][j]) % MOD; 35 dp[i + 1][tmp2] = ((dp[i + 1][tmp2] + cnt[i][j]) % MOD + dp[i][j]) % MOD; 36 } 37 } 38 printf("%lld\n", dp[b][0]); 39 } 40 return 0; 41 }
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 #define N 1010 5 #define ll long long 6 const ll MOD = (ll)1e9 + 9; 7 ll dp[N][N], cnt[N][N]; 8 int n, k; 9 10 int main() 11 { 12 while (scanf("%d%d", &k, &n) != EOF) 13 { 14 memset(dp, 0, sizeof dp); 15 memset(cnt, 0, sizeof cnt); 16 ++dp[1][1 % k]; 17 ++cnt[1][1 % k]; 18 ++cnt[1][0]; 19 ll tmp = 1 % k; 20 for (int i = 2; i <= n; ++i) 21 { 22 tmp = tmp * 2 % k; 23 for (int j = 0; j < k; ++j) 24 { 25 dp[i][j] = dp[i - 1][j]; 26 cnt[i][j] = cnt[i - 1][j]; 27 if (j - tmp >= 0) 28 { 29 dp[i][j] = (dp[i][j] + dp[i - 1][j - tmp] + cnt[i - 1][j - tmp]) % MOD; 30 cnt[i][j] = (cnt[i][j] + cnt[i - 1][j - tmp]) % MOD; 31 } 32 else 33 { 34 dp[i][j] = (dp[i][j] + dp[i - 1][k + j - tmp] + cnt[i - 1][k + j - tmp]) % MOD; 35 cnt[i][j] = (cnt[i][j] + cnt[i - 1][k + j - tmp]) % MOD; 36 } 37 } 38 } 39 printf("%lld\n", dp[n][0]); 40 } 41 return 0; 42 }
E:Cops And Robbers
Solved.
题意:
在一个$n * m 的矩阵中,有些地方可以建墙,但是不同的墙开销不同,求最小开销把劫匪围住$
思路:
拆点最小割,但是是点权,拆点即可。
1 #include<bits/stdc++.h> 2 3 using namespace std; 4 5 const int maxn = 4e3 + 10; 6 const int INF = 0x3f3f3f3f; 7 8 struct Edge{ 9 int to, flow, nxt; 10 Edge(){} 11 Edge(int to, int nxt, int flow):to(to), nxt(nxt), flow(flow){} 12 }edge[maxn << 2]; 13 14 int head[maxn], dep[maxn]; 15 int S,T; 16 int N, n, m, tot; 17 18 void Init(int n) 19 { 20 N = n; 21 //memset(head, -1, sizeof head); 22 for(int i = 0; i < N; ++i) head[i] = -1; 23 tot = 0; 24 } 25 26 void addedge(int u, int v, int w, int rw = 0) 27 { 28 edge[tot] = Edge(v, head[u], w); head[u] = tot++; 29 edge[tot] = Edge(u, head[v], rw); head[v] = tot++; 30 } 31 32 bool BFS() 33 { 34 //memset(dep, -1, sizeof dep); 35 for(int i = 0; i < N; ++i) dep[i] = -1; 36 queue<int>q; 37 q.push(S); 38 dep[S] = 1; 39 while(!q.empty()) 40 { 41 int u = q.front(); 42 q.pop(); 43 for(int i = head[u]; ~i; i = edge[i].nxt) 44 { 45 if(edge[i].flow && dep[edge[i].to] == -1) 46 { 47 dep[edge[i].to] = dep[u] + 1; 48 q.push(edge[i].to); 49 } 50 } 51 } 52 return dep[T] < 0 ? 0 : 1; 53 } 54 55 int DFS(int u, int f) 56 { 57 if(u == T || f == 0) return f; 58 int w, used = 0; 59 for(int i = head[u]; ~i; i = edge[i].nxt) 60 { 61 if(edge[i].flow && dep[edge[i].to] == dep[u] + 1) 62 { 63 w = DFS(edge[i].to, min(f - used, edge[i].flow)); 64 edge[i].flow -= w; 65 edge[i ^ 1].flow += w; 66 used += w; 67 if(used == f) return f; 68 } 69 } 70 if(!used) dep[u] = -1; 71 return used; 72 } 73 74 int Dicnic() 75 { 76 int ans = 0; 77 while(BFS()) 78 { 79 int tmp = DFS(S, INF); 80 if(tmp == INF) return -1; 81 ans += tmp; 82 } 83 return ans; 84 } 85 86 int c; 87 int C[maxn]; 88 char mp[100][100]; 89 90 int calc(int i, int j) 91 { 92 return i * 31 + j; 93 } 94 95 int main() 96 { 97 while(~scanf("%d %d %d", &m, &n, &c)) 98 { 99 int base = n * 31 + m + 31; 100 int x, y; 101 Init(4000); 102 for(int i = 1; i <= n; ++i) 103 { 104 for(int j = 1; j <= m; ++j) 105 { 106 scanf(" %c", &mp[i][j]); 107 if(mp[i][j] == 'B') { x = i, y = j; } 108 } 109 } 110 for(int i = 1; i <= c; ++i) scanf("%d", C + i); 111 S = 0, T = calc(x, y); 112 for(int i = 1; i <= n; ++i) 113 { 114 addedge(S, calc(i, 1), INF); 115 addedge(S, calc(i, m), INF); 116 } 117 for(int i = 1; i <= m; ++i) 118 { 119 addedge(S, calc(1, i), INF); 120 addedge(S, calc(n, i), INF); 121 } 122 for(int i = 1; i <= n; ++i) 123 { 124 for(int j = 1; j <= m; ++j) 125 { 126 if(i != n) 127 { 128 addedge(calc(i, j) + base, calc(i + 1, j), INF); 129 addedge(calc(i + 1, j) + base, calc(i, j), INF); 130 } 131 if(j != m) 132 { 133 addedge(calc(i, j)+ base, calc(i, j + 1), INF); 134 addedge(calc(i, j + 1) + base, calc(i, j), INF); 135 } 136 } 137 } 138 for(int i = 1; i <= n; ++i) 139 { 140 for(int j = 1; j <= m; ++j) 141 { 142 if(mp[i][j] >= 'a' && mp[i][j] <= 'z') 143 { 144 addedge(calc(i, j), calc(i, j) + base, C[mp[i][j] - 'a' + 1]); 145 } 146 else 147 { 148 addedge(calc(i, j), calc(i, j) + base, INF); 149 } 150 } 151 } 152 int ans = Dicnic(); 153 printf("%d\n", ans); 154 } 155 return 0; 156 }
F:Rectangles
Solved.
题意:
二维平面上有一些平行坐标轴的矩形,求有多少区间是被奇数个矩形覆盖。
思路:
扫描线,只是把区间加减换成区间01状态翻转,现场学扫描线可还行..
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 #define ll long long 5 #define N 200010 6 #define pii pair <int, int> 7 int n, mx, my, bx[N], by[N]; 8 ll res; 9 struct Rec 10 { 11 int x[2], y[2]; 12 void scan() 13 { 14 for (int i = 0; i < 2; ++i) scanf("%d%d", x + i, y + i); 15 if (x[0] > x[1]) swap(x[0], x[1]); 16 if (y[0] > y[1]) swap(y[0], y[1]); 17 bx[++mx] = x[0]; 18 bx[++mx] = x[1]; 19 by[++my] = y[0]; 20 by[++my] = y[1]; 21 } 22 }rec[N]; 23 vector <pii> v[N]; 24 25 void Hash() 26 { 27 sort(bx + 1, bx + 1 + mx); 28 sort(by + 1, by + 1 + my); 29 mx = unique(bx + 1, bx + 1 + mx) - bx - 1; 30 my = unique(by + 1, by + 1 + my) - by - 1; 31 for (int i = 1; i <= n; ++i) 32 { 33 for (int j = 0; j < 2; ++j) 34 { 35 rec[i].x[j] = lower_bound(bx + 1, bx + 1 + mx, rec[i].x[j]) - bx; 36 rec[i].y[j] = lower_bound(by + 1, by + 1 + my, rec[i].y[j]) - by; 37 } 38 } 39 } 40 41 namespace SEG 42 { 43 struct node 44 { 45 ll sum, val; 46 int lazy; 47 node () {} 48 node (ll sum, ll val, int lazy) : sum(sum), val(val), lazy(lazy) {} 49 void init() { sum = val = lazy = 0; } 50 node operator + (const node &other) const { return node(sum + other.sum, val + other.val, 0); } 51 void Xor() { val = sum - val; lazy ^= 1; } 52 }a[N << 2]; 53 void build(int id, int l, int r) 54 { 55 a[id] = node(0, 0, 0); 56 if (l == r) 57 { 58 a[id].sum = by[l + 1] - by[l]; 59 return; 60 } 61 int mid = (l + r) >> 1; 62 build(id << 1, l, mid); 63 build(id << 1 | 1, mid + 1, r); 64 a[id] = a[id << 1] + a[id << 1 | 1]; 65 } 66 void pushdown(int id) 67 { 68 if (!a[id].lazy) return; 69 a[id << 1].Xor(); 70 a[id << 1 | 1].Xor(); 71 a[id].lazy = 0; 72 } 73 void update(int id, int l, int r, int ql, int qr) 74 { 75 if (l >= ql && r <= qr) 76 { 77 a[id].Xor(); 78 return; 79 } 80 int mid = (l + r) >> 1; 81 pushdown(id); 82 if (ql <= mid) update(id << 1, l, mid, ql, qr); 83 if (qr > mid) update(id << 1 | 1, mid + 1, r, ql, qr); 84 a[id] = a[id << 1] + a[id << 1 | 1]; 85 } 86 } 87 88 int main() 89 { 90 while (scanf("%d", &n) != EOF) 91 { 92 mx = my = 0; res = 0; 93 for (int i = 1; i <= 2 * n; ++i) v[i].clear(); 94 for (int i = 1; i <= n; ++i) rec[i].scan(); Hash(); 95 for (int i = 1; i <= n; ++i) 96 { 97 v[rec[i].x[0]].emplace_back(rec[i].y[0], rec[i].y[1] - 1); 98 v[rec[i].x[1]].emplace_back(rec[i].y[0], rec[i].y[1] - 1); 99 } 100 n <<= 1; 101 SEG::build(1, 1, n); 102 for (int i = 1; i < mx; ++i) 103 { 104 for (auto it : v[i]) SEG::update(1, 1, n, it.first, it.second); 105 res += (bx[i + 1] - bx[i]) * SEG::a[1].val; 106 } 107 printf("%lld\n", res); 108 } 109 return 0; 110 }
G:Goat on a Rope
Solved.
签到。
1 #include<bits/stdc++.h> 2 3 using namespace std; 4 5 double x, y; 6 double xa, xb, ya, yb; 7 8 double calc(double xa, double ya, double xb, double yb) 9 { 10 return sqrt((xa - xb) * (xa - xb) + (ya - yb) * (ya - yb)); 11 } 12 13 int main() 14 { 15 while(~scanf("%lf %lf %lf %lf %lf %lf", &x, &y, &xa, &ya, &xb, &yb)) 16 { 17 double ans = 1e9; 18 if(x >= min(xa, xb) && x <= max(xa, xb)) ans = min(ans, min(fabs(y - ya), fabs(y - yb))); 19 if(y >= min(ya, yb) && y <= max(ya, yb)) ans = min(ans, min(fabs(x - xa), fabs(x - xb))); 20 ans = min(ans, calc(x, y, xa, ya)); 21 ans = min(ans, calc(x, y, xa, yb)); 22 ans = min(ans, calc(x, y, xb, ya)); 23 ans = min(ans, calc(x, y, xb, yb)); 24 printf("%.3f\n", ans); 25 } 26 return 0; 27 }
H:Repeating Goldbachs
Solved.
签到。
1 #include<bits/stdc++.h> 2 3 using namespace std; 4 5 const int maxn = 1e6 + 10; 6 7 bool isprime[maxn]; 8 int prime[maxn]; 9 10 void Init() 11 { 12 memset(isprime, true, sizeof isprime); 13 isprime[0] = isprime[1] = false; 14 for(int i = 2; i < maxn; ++i) 15 { 16 if(isprime[i]) 17 { 18 prime[++prime[0]] = i; 19 for(int j = i * 2; j < maxn; j += i) 20 { 21 isprime[j] = false; 22 } 23 } 24 } 25 } 26 27 int x; 28 29 int main() 30 { 31 Init(); 32 while(~scanf("%d", &x)) 33 { 34 int ans = 0; 35 while(x >= 4) 36 { 37 for(int i = 1; i <= prime[0]; ++i) 38 { 39 int tmp = prime[i]; 40 if(isprime[x - tmp]) 41 { 42 ++ans; 43 x = x - tmp - tmp; 44 break; 45 } 46 } 47 } 48 printf("%d\n", ans); 49 } 50 return 0; 51 }
I:Inversions
Unsolved.
题意:
给出一些序列,一些位置上的数字可以在$[1, k]的范围内随便填,求如果填使得整个序列的逆序对个数最多$
J:Time Limits
Solved.
签到。
1 #include<bits/stdc++.h> 2 3 using namespace std; 4 5 int n, s; 6 7 int main() 8 { 9 while(~scanf("%d %d", &n, &s)) 10 { 11 int Max = -1; 12 for(int i = 1; i <= n; ++i) 13 { 14 int num; 15 scanf("%d", &num); 16 Max = max(Max, num); 17 } 18 Max *= s; 19 int ans = Max / 1000; 20 if(Max % 1000) ans++; 21 printf("%d\n", ans); 22 } 23 return 0; 24 }
K:Knockout
Unsolved.
题意:
给出一个数字,然后两个骰子的点数,每次可以移掉数字当中某几位加起来的和等于两骰子点数之和
那么就可以移掉这几位,知道最后不能移位置,最后剩下的数就是分数
现在给出一中间局面,求移掉哪些,使得最后的分数期望最大以及最小
L:Liars
Solved.
签到。
1 #include<bits/stdc++.h> 2 3 using namespace std; 4 5 const int maxn = 1e3 + 10; 6 7 int n; 8 int cnt[maxn]; 9 10 int main() 11 { 12 while(~scanf("%d", &n)) 13 { 14 memset(cnt, 0, sizeof cnt); 15 for(int i = 1; i <= n; ++i) 16 { 17 int ai, bi; 18 scanf("%d %d", &ai, &bi); 19 for(int j = ai; j <= bi; ++j) 20 { 21 cnt[j]++; 22 } 23 } 24 int ans = -1; 25 for(int i = 1; i <= n; ++i) 26 { 27 if(cnt[i] == i) ans = i; 28 } 29 printf("%d\n", ans); 30 } 31 return 0; 32 }
M:Mobilization
Unsolved.
题意:
$有m种军队,每种军队有h_i 属性 和 p_i属性,以及购买一支军队需要c_i的钱,每种军队可以购买无限支$
$现在你有C个单位的钱,求如何购买军队,使得\sum h_i \cdot \sum p_i 最大$