ZOJ Monthly, June 2018 Solution
A - Peer Review
Water.
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 int t, n; 5 6 int main() 7 { 8 scanf("%d", &t); 9 while (t--) 10 { 11 scanf("%d", &n); 12 for (int i = 1; i <= n; ++i) printf("0%c", " \n"[i == n]); 13 } 14 return 0; 15 }
B - Boring Game
Unsolved.
C - Enigma Machine
Unsolved.
D - Number Theory
Unsolved.
E - Chasing
Solved.
题意:两个人在一个二维平面上,刚开始两个人在第二象限或者第三象限,A跑到Y轴B就不能追了,B的速度是A的K倍,求B能否追到B
思路:k < 0 那么必然不行
k == 0 判断是不是在同一高度,如果是判断起始位置
k > 0 假设y轴上存在某个点 使得A B 能够在该点相遇,列方程求解
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<bits/stdc++.h> 2 3 using namespace std; 4 5 double xa, xb, ya, yb, k; 6 7 int main() 8 { 9 int t; 10 scanf("%d", &t); 11 while(t--) 12 { 13 scanf("%lf %lf %lf %lf %lf", &xa, &ya, &xb, &yb, &k); 14 if(k < 1.0) 15 { 16 puts("N"); 17 } 18 else if(k == 1.0) 19 { 20 if(ya != yb) puts("N"); 21 else if(xb < xa) puts("N"); 22 else puts("Y"); 23 } 24 else 25 { 26 double a = k * k - 1.0; 27 double b = -2.0 * k * k * ya + 2.0 * yb; 28 double c = k * k * xa * xa - xb * xb + k * k * ya * ya - yb * yb; 29 double d = b * b - 4.0 * a * c; 30 if(d >= 0) puts("N"); 31 else puts("Y"); 32 } 33 } 34 return 0; 35 }
F - Carrot Gathering
Unsolved.
G - Virtual Singers
Upsolved.
题意:
H - How Many Palindromes
Unsolved.
题意:
有$n个A数和m个B数 m <= n,对每个B数都匹配一个A数,使得\sum_{i = 1}^{i = m} |B[i] - A[i]| 最小$
思路:
考虑费用流的思想,维护三个堆
将$A、B 数放在一起排序,然后从左往右枚举$
$如果当前数是A$
如果左边有未匹配的$B_1数,那么与之匹配即可,此操作不用考虑反悔操作$
$因为假如有另一个B_2,使得B_2 与 这个A匹配更优$
那么后面必然要有另一个$A_2 来匹配这个B_1 $
$贡献是 A_2 - B_1 + B_2 - A_1 这个式子显然小于 A_2 - B_2 + A_1 - B_1$
因为这四个数字存在大小关系
$A_2 - B_1 大于 A_2 - B_2 + A_1 - B_1$
如果左边没有未匹配的$B_1数,那么考虑B的反悔操作是否更优$
$更优的话就反悔并且将代价取反并算上A往右匹配的贡献放入堆Q_A中, 相当于反悔操作$
$如果当前数是B$
$如果左边有未匹配的A数,那么与之匹配,并且将代价取反并且算上B往右匹配的贡献放入堆Q_C中$
否则放入堆$Q_B$中表示还未匹配
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 #define ll long long 5 #define N 100010 6 int t, n, m; ll x; 7 struct node 8 { 9 ll v; int Type; 10 node () {} 11 node (ll v, int Type) : v(v), Type(Type) {} 12 bool operator < (const node &r) const { return v < r.v; } 13 }arr[N << 1]; 14 priority_queue <ll, vector <ll> , greater <ll> > A, B, C; 15 16 int main() 17 { 18 scanf("%d", &t); 19 while (t--) 20 { 21 while (!A.empty()) A.pop(); 22 while (!B.empty()) B.pop(); 23 while (!C.empty()) C.pop(); 24 scanf("%d%d", &n, &m); 25 for (int i = 1; i <= n; ++i) 26 { 27 scanf("%lld", &x); 28 arr[i] = node(x, 0); 29 } 30 for (int i = 1; i <= m; ++i) 31 { 32 scanf("%lld", &x); 33 arr[n + i] = node(x, 1); 34 } 35 sort(arr + 1, arr + 1 + n + m); 36 ll res = 0; 37 for (int i = 1; i <= n + m; ++i) 38 { 39 if (arr[i].Type == 1) 40 { 41 if (!A.empty()) 42 { 43 x = A.top(); A.pop(); 44 res += arr[i].v + x; 45 C.push(-x - 2 * arr[i].v); 46 } 47 else 48 B.push(-arr[i].v); 49 } 50 else 51 { 52 if (!B.empty()) 53 { 54 x = B.top(); B.pop(); 55 res += arr[i].v + x; 56 } 57 else if (!C.empty()) 58 { 59 x = C.top(); 60 if (arr[i].v + x < 0) 61 { 62 C.pop(); 63 res += arr[i].v + x; 64 A.push(-x - 2 * arr[i].v); 65 } 66 else 67 A.push(-arr[i].v); 68 } 69 else 70 A.push(-arr[i].v); 71 } 72 } 73 printf("%lld\n", res); 74 } 75 return 0; 76 }
I - District Division
Solved.
题意:给出一棵树,求能不能分成$\frac{n}{k}$ 个连通块,使得每块个数为k
思路:如果存在合法分法,那么必然存在某个子树大小为k,DFS下去,再回溯上来判断即可,遇到大小为k的子树就分块
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<bits/stdc++.h> 2 3 using namespace std; 4 5 const int maxn = 1e5 + 10; 6 7 int n, k; 8 9 int val[maxn]; 10 int fa[maxn]; 11 int vis[maxn]; 12 int cnt; 13 vector<int>root; 14 vector<int>G[maxn]; 15 16 void Init() 17 { 18 cnt = 0; 19 root.clear(); 20 for(int i = 1; i <= n; ++i) 21 { 22 val[i] = vis[i] = 0; 23 fa[i] = i; 24 G[i].clear(); 25 } 26 } 27 28 void DFS(int u, int pre) 29 { 30 val[u] = 1; 31 fa[u] = pre; 32 for(int i = 0, len = G[u].size(); i < len; ++i) 33 { 34 int v = G[u][i]; 35 if(v == pre) continue; 36 DFS(v, u); 37 val[u] += val[v]; 38 } 39 if(val[u] == k) 40 { 41 cnt++; 42 val[u] = 0; 43 root.push_back(u); 44 } 45 } 46 47 void DFS2(int u, int pre) 48 { 49 if(vis[u]) return ; 50 vis[u] = 1; 51 printf("%d%c", u, " \n"[cnt == k]); 52 cnt++; 53 for(int i = 0, len = G[u].size(); i < len; ++i) 54 { 55 int v = G[u][i]; 56 if(v == pre) continue; 57 DFS2(v, u); 58 } 59 } 60 61 int main() 62 { 63 int t; 64 scanf("%d", &t); 65 while(t--) 66 { 67 scanf("%d %d", &n, &k); 68 Init(); 69 for(int i = 1; i < n; ++i) 70 { 71 int u, v; 72 scanf("%d %d", &u, &v); 73 G[u].push_back(v); 74 G[v].push_back(u); 75 } 76 DFS(1, -1); 77 if(cnt != n / k) 78 { 79 puts("NO"); 80 continue; 81 } 82 puts("YES"); 83 for(int i = 0, len = root.size(); i < len; ++i) 84 { 85 cnt = 1; 86 DFS2(root[i], fa[root[i]]); 87 } 88 } 89 return 0; 90 }
J - Good Permutation
Solved.
题意:每次能够交换相邻两个数,求最少交换次数使得满足题目给出的序列条件
思路:通过枚举每次在第一位的数,从而递推下一种状态,比如12345推出51234即加上5移到第一位的步数再减去5带来的贡献
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<bits/stdc++.h> 2 3 using namespace std; 4 5 typedef long long ll; 6 7 const int maxn = 1e5 + 10; 8 9 int n; 10 int arr[maxn]; 11 int pos[maxn]; 12 int a[maxn]; 13 14 int lowbit(int p) 15 { 16 return p & (-p); 17 } 18 19 void update(int p) 20 { 21 while(p <= n) 22 { 23 a[p]++; 24 p += lowbit(p); 25 } 26 } 27 28 int query(int p) 29 { 30 int res = 0; 31 while(p) 32 { 33 res += a[p]; 34 p -= lowbit(p); 35 } 36 return res; 37 } 38 39 int main() 40 { 41 int t; 42 scanf("%d", &t); 43 while(t--) 44 { 45 scanf("%d", &n); 46 ll ans = 0; 47 for(int i = 1; i <= n; ++i) a[i] = 0; 48 for(int i = 1; i <= n; ++i) 49 { 50 scanf("%d", arr + i); 51 pos[arr[i]] = i; 52 } 53 for(int i = n; i >= 1; --i) 54 { 55 ans += query(arr[i] - 1); 56 update(arr[i]); 57 } 58 ll tmp = ans; 59 for(int i = 1; i <= n; ++i) 60 { 61 tmp = tmp + n - pos[i] - (pos[i] - 1); 62 ans = min(ans, tmp); 63 } 64 printf("%lld\n", ans); 65 } 66 return 0; 67 }