2018 Multi-University Training Contest 10 Solution
A - Problem A.Alkane
留坑。
B - Problem B. Beads
留坑。
C - Problem C. Calculate
留坑。
D - Problem D. Permutation
留坑。
E - Problem E. TeaTree
题意:每个点会存下任意两个以他为LCA的点对的GCD,求每个点存的GCD的最大值
思路:DSU on tree 用 set 维护子树中的因子,对于重儿子不要处理多次 每次查找的时候,枚举轻儿子中的因子
还有一种线段树合并的写法
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 #define N 100010 5 6 int n; 7 vector <int> G[N], num[N]; 8 set <int> s; 9 int sz[N], son[N], cnt[N], cnt2[N], arr[N], ans[N], Max; 10 bool isbig[N]; 11 12 void Init() 13 { 14 for (int i = 1; i <= 100000; ++i) 15 { 16 int limit = sqrt(i); 17 for (int j = 1; j < limit; ++j) if (i % j == 0) 18 { 19 num[i].push_back(j); 20 num[i].push_back(i / j); 21 } 22 if (i % limit == 0) 23 { 24 num[i].push_back(limit); 25 if (limit * limit != i) num[i].push_back(i / limit); 26 } 27 } 28 } 29 30 void DFS(int u) 31 { 32 sz[u] = 1; 33 for (auto v : G[u]) 34 { 35 DFS(v); sz[u] += sz[v]; 36 if (son[u] == -1 || sz[v] > sz[son[u]]) son[u] = v; 37 } 38 } 39 40 void update(int u) 41 { 42 for (auto it : num[arr[u]]) ++cnt[it]; 43 for (auto v : G[u]) if (!isbig[v]) update(v); 44 } 45 46 void work(int u, int fa) 47 { 48 for (auto it : num[arr[u]]) s.insert(it); 49 for (auto v : G[u]) if (!isbig[v]) work(v, fa); 50 } 51 52 void query(int u) 53 { 54 for (auto v : G[u]) if (!isbig[v]) 55 { 56 s.clear(); 57 work(v, u); 58 for (auto it : s) if (cnt[it] >= 1 || cnt2[it] >= 1) Max = max(Max, it); 59 for (auto it : s) ++cnt2[it]; 60 } 61 for (auto it : num[arr[u]]) if (cnt[it] >= 1 || cnt2[it] >= 1) Max = max(Max, it); 62 } 63 64 void clear(int u) 65 { 66 for (auto it : num[arr[u]]) cnt[it] = cnt2[it] = 0; 67 for (auto v : G[u]) clear(v); 68 } 69 70 void DSU(int u) 71 { 72 for (auto v : G[u]) if (v != son[u]) DSU(v); 73 if (son[u] != -1) { isbig[son[u]] = 1; DSU(son[u]); } 74 Max = -1; query(u); 75 ans[u] = Max; 76 if (isbig[u]) update(u); 77 if (son[u] != -1) isbig[son[u]] = 0; 78 if (!isbig[u]) clear(u); 79 } 80 81 int main() 82 { 83 Init(); 84 while (scanf("%d", &n) != EOF) 85 { 86 for (int i = 1; i <= n; ++i) G[i].clear(); 87 memset(son, -1, sizeof son); 88 memset(cnt, 0, sizeof cnt); 89 memset(cnt2, 0, sizeof cnt2); 90 Max = -1; s.clear(); 91 for (int i = 2, u; i <= n; ++i) 92 { 93 scanf("%d", &u); 94 G[u].push_back(i); 95 } 96 for (int i = 1; i <= n; ++i) scanf("%d", arr + i); 97 DFS(1); DSU(1); 98 for (int i = 1; i <= n; ++i) printf("%d\n", ans[i]); 99 } 100 return 0; 101 }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 #define N 100010 5 vector <int> num[N], G[N]; 6 7 int n; 8 int arr[N], rt[N], ans[N]; 9 10 void Init() 11 { 12 for (int i = 1; i <= 100000; ++i) for (int j = i; j <= 100000; j += i) 13 num[j].push_back(i); 14 } 15 16 struct SEG 17 { 18 #define M N * 400 19 int lson[M], rson[M], c[M], cnt; 20 void init() { cnt = 0; } 21 void pushup(int id) { c[id] = max(c[lson[id]], c[rson[id]]); } 22 void update(int &x, int l, int r, int pos) 23 { 24 if (!x) x = ++cnt; 25 if (l == r) { c[x] = pos; return; } 26 int mid = (l + r) >> 1; 27 pos <= mid ? update(lson[x], l, mid, pos) : update(rson[x], mid + 1, r, pos); 28 pushup(x); 29 } 30 int merge(int u, int v, int &sum) 31 { 32 if (!u || !v) return u | v; 33 if (c[u] == c[v]) sum = max(sum, c[u]); 34 if (lson[u] | lson[v]) lson[u] = merge(lson[u], lson[v], sum); 35 if (rson[u] | rson[v]) rson[u] = merge(rson[u], rson[v], sum); 36 return u; 37 } 38 }seg; 39 40 void DFS(int u) 41 { 42 ans[u] = -1; 43 for (auto v : G[u]) 44 { 45 DFS(v); 46 seg.merge(rt[u], rt[v], ans[u]); 47 } 48 } 49 50 void Run() 51 { 52 Init(); 53 while (scanf("%d", &n) != EOF) 54 { 55 seg.init(); 56 for (int i = 2, u; i <= n; ++i) 57 { 58 scanf("%d", &u); 59 G[u].push_back(i); 60 } 61 for (int i = 1; i <= n; ++i) scanf("%d", arr + i); 62 for (int i = 1; i <= n; ++i) for (auto it : num[arr[i]]) seg.update(rt[i], 1, 100000, it); 63 DFS(1); 64 for (int i = 1; i <= n; ++i) printf("%d\n", ans[i]); 65 } 66 } 67 68 int main() 69 { 70 #ifdef LOCAL 71 freopen("Test.in", "r", stdin); 72 #endif 73 74 Run(); 75 return 0; 76 77 }
F - Problem F. NewNippori
留坑。
G - Problem G. Cyclic
打表找规律即可
$F[n] = (n - 2) * F[n - 1] + (n - 1) * F[n - 2] + (i \& 1 ? 1 : -1)$
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 #define N 100010 5 #define ll long long 6 7 const ll MOD = 998244353; 8 9 ll arr[N]; 10 11 int main() 12 { 13 arr[4] = 1, arr[5] = 8; 14 for (int i = 6; i <= 100000; ++i) arr[i] = ((i - 2) * arr[i - 1] % MOD + (i - 1) * arr[i - 2] % MOD + ((i & 1) ? 1 : -1) + MOD) % MOD; 15 int t; scanf("%d", &t); 16 while (t--) 17 { 18 int n; 19 scanf("%d", &n); 20 printf("%lld\n", arr[n]); 21 } 22 }
H - Problem H. Pow
水。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import java.util.Scanner; import java.math.*; public class Main { public static void main(String[] args) { Scanner in = new Scanner(System.in); int t = in.nextInt(); while (t-- != 0) { int n = in.nextInt(); System.out.println(BigInteger.valueOf(2).pow(n)); } } }
I - Problem I. Count
题意:求$\sum_{i = 1} ^ {i = n} \sum_{j = 1} ^ {j = i - 1} [gcd(i + j, i - j) == 1]$
思路:找规律。如果是奇数就加上$\frac {\varphi(n)}{2} 否则加上 \varphi(n)$
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <bits/stdc++.h> 2 3 using namespace std; 4 5 typedef long long ll; 6 const int MAXN = 20000000; 7 bool check[MAXN + 10]; 8 9 ll ans[MAXN + 10]; 10 int phi[MAXN + 10]; 11 int prime[MAXN + 10]; 12 int tot; 13 14 void phi_ans_prime_table() { 15 memset(check, false, sizeof check); 16 phi[1] = 1; 17 tot = 0; 18 for(int i = 2; i <= MAXN; ++i) 19 { 20 if(!check[i]) 21 { 22 prime[tot++] = i; 23 phi[i] = 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 phi[i * prime[j]] = phi[i] * prime[j]; 32 break; 33 } 34 else 35 { 36 phi[i * prime[j]] = phi[i] * (prime[j] - 1); 37 } 38 } 39 if(i & 1) ans[i] = ans[i - 1] + phi[i] / 2; 40 else ans[i] = ans[i - 1] + phi[i]; 41 } 42 } 43 44 int main() { 45 phi_ans_prime_table(); 46 int t; 47 scanf("%d", &t); 48 while(t--) 49 { 50 int n; 51 scanf("%d", &n); 52 printf("%lld\n", ans[n]); 53 } 54 return 0; 55 }
J - Problem J. CSGO
题意:有n把主武器和m把副武器,每把武器有k种属性,选取一把主武器和副武器,求题中式子最大值
思路:对于没种属性又有加减两种状态,枚举每把武器的每种属性的加减状态,求最大值
![](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 ll INFLL = 0x3f3f3f3f3f3f3f3f; 8 const int maxn = 1e5 + 10; 9 10 ll arr[maxn][10], brr[maxn][10]; 11 ll crr[maxn]; 12 13 int n, m, k; 14 15 void RUN() 16 { 17 int t; 18 scanf("%d", &t); 19 while (t--) 20 { 21 scanf("%d %d %d", &n, &m, &k); 22 for (int i = 1; i <= n; ++i) for (int j = 0; j <= k; ++j) scanf("%lld", &arr[i][j]); 23 for (int i = 1; i <= m; ++i) for (int j = 0; j <= k; ++j) scanf("%lld", &brr[i][j]); 24 memset(crr, 0, sizeof crr); 25 ll ans = 0; 26 int limit = (1 << k); 27 for (int i = 1; i <= n; ++i) 28 { 29 for (int S = 0; S < limit; ++S) 30 { 31 ll tmp = arr[i][0]; 32 for (int j = 0; j < k; ++j) 33 { 34 if (S & (1 << j)) 35 { 36 tmp += arr[i][j + 1]; 37 } 38 else 39 { 40 tmp -= arr[i][j + 1]; 41 } 42 } 43 crr[S] = max(crr[S], tmp); 44 } 45 } 46 47 for (int i = 1; i <= m; ++i) 48 { 49 for (int S = 0; S < limit; ++S) 50 { 51 ll tmp = brr[i][0]; 52 for (int j = 0; j < k; ++j) 53 { 54 if (S & (1 << j)) 55 { 56 tmp += brr[i][j + 1]; 57 } 58 else 59 { 60 tmp -= brr[i][j + 1]; 61 } 62 } 63 ans = max(ans, tmp + crr[limit - 1 - S]); 64 } 65 } 66 printf("%lld\n", ans); 67 } 68 } 69 70 int main() 71 { 72 #ifdef LOCAL_JUDGE 73 freopen("Text.txt", "r", stdin); 74 #endif // LOCAL_JUDGE 75 76 RUN(); 77 78 #ifdef LOCAL_JUDGE 79 fclose(stdin); 80 #endif // LOCAL_JUDGE 81 return 0; 82 }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <bits/stdc++.h> using namespace std; #define N 100010 #define ll long long #define INF 0x3f3f3f3f3f3f3f3f int t, n, m, K; struct node { int x[10]; void scan(int vis) { scanf("%d", x + vis); for (int i = 2; i <= K + 1; ++i) scanf("%d", x + i); } }a[N], b[N]; void Run() { scanf("%d", &t); while (t--) { scanf("%d%d%d", &n, &m, &K); for (int i = 1; i <= n; ++i) a[i].scan(0); for (int i = 1; i <= m; ++i) b[i].scan(1); ll res = 0; for (int i = 0; i < (1 << (K + 2)); ++i) { bitset <7> bit; bit = i; ll Max[2] = { -INF, -INF }; ll Min[2] = { INF, INF }; for (int j = 1; j <= n; ++j) { ll tmp = 0; for (int k = 0; k <= K + 1; ++k) tmp += a[j].x[k] * (bit[k] ? 1 : -1); Max[0] = max(Max[0], tmp); Min[0] = min(Min[0], tmp); } for (int j = 1; j <= m; ++j) { ll tmp = 0; for (int k = 0; k <= K + 1; ++k) tmp += b[j].x[k] * (bit[k] ? 1 : -1); Max[1] = max(Max[1], tmp); Min[1] = min(Min[1], tmp); } res = max(res, max(Max[0] - Min[1], Max[1] - Min[0])); } printf("%lld\n", res); } } int main() { #ifdef LOCAL freopen("Test.in", "r", stdin); #endif Run(); return 0; }
K - Problem K. Pow2
留坑。
L - Problem L.Videos
题意:每天有n个小时,有m部电影,k个人,每部电影只能被一个人看,得到w快乐值,电影种类有AB两种,同一人连着看同种电影要减去W快乐值,如何安排使得快乐值最大
思路:将一部电影拆成两个点 S 和 T 流为1,费用为-w, 电影与电影之间如果是可以连着看的,就连边,如果是同种类,费用就是W, 否则就是0,流为1,源点练出来加一个点,流量为k,限制为k个人
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<bits/stdc++.h> 2 3 using namespace std; 4 5 const int INF = 0x3f3f3f3f; 6 const int maxn = 1e4 + 10; 7 const int maxm = 1e5 + 10; 8 9 struct Edge{ 10 int to, nxt, cap, flow, cost; 11 }edge[maxm]; 12 13 int head[maxn], tot; 14 int pre[maxn], dis[maxn]; 15 bool vis[maxn]; 16 int N; 17 18 void Init(int n) 19 { 20 N = n; 21 tot = 0; 22 for(int i = 0; i < n; ++i) head[i] = -1; 23 } 24 25 void addedge(int u, int v, int cap, int cost) 26 { 27 edge[tot].to = v; 28 edge[tot].cap = cap; 29 edge[tot].cost = cost; 30 edge[tot].flow = 0; 31 edge[tot].nxt = head[u]; 32 head[u] = tot++; 33 34 edge[tot].to = u; 35 edge[tot].cap = 0; 36 edge[tot].cost = -cost; 37 edge[tot].flow = 0; 38 edge[tot].nxt = head[v]; 39 head[v] = tot++; 40 } 41 42 bool SPFA(int s,int t) 43 { 44 queue<int>q; 45 for(int i = 0; i < N; ++i) dis[i] = INF, pre[i] = -1, vis[i] = false; 46 dis[s] = 0; 47 vis[s] = true; 48 q.push(s); 49 while(!q.empty()) 50 { 51 int u = q.front(); 52 q.pop(); 53 vis[u] = false; 54 for(int i = head[u]; ~i; i = edge[i].nxt) 55 { 56 int v = edge[i].to; 57 if(edge[i].cap > edge[i].flow && dis[v] > dis[u] + edge[i].cost) 58 { 59 dis[v] = dis[u] + edge[i].cost; 60 pre[v] = i; 61 if(!vis[v]) 62 { 63 vis[v] = true; 64 q.push(v); 65 } 66 } 67 } 68 } 69 if(pre[t] == -1) return false; 70 else return true; 71 } 72 73 int minCostMaxflow(int s,int t, int &cost) 74 { 75 int flow = 0; 76 cost = 0; 77 while(SPFA(s, t)) 78 { 79 int Min = INF; 80 for(int i = pre[t]; ~i; i = pre[edge[i ^ 1].to]) 81 { 82 if(Min > edge[i].cap - edge[i].flow) 83 { 84 Min = edge[i].cap - edge[i].flow; 85 } 86 } 87 for(int i = pre[t]; ~i; i = pre[edge[i ^ 1].to]) 88 { 89 edge[i].flow += Min; 90 edge[i ^ 1].flow -= Min; 91 cost += edge[i].cost * Min; 92 } 93 flow += Min; 94 } 95 return flow; 96 } 97 98 struct node{ 99 int si, ti, wi, op; 100 }arr[maxn]; 101 102 int n, m, K, W; 103 104 int main() 105 { 106 int t; 107 scanf("%d", &t); 108 while(t--) 109 { 110 scanf("%d %d %d %d", &n, &m, &K, &W); 111 for(int i = 1; i <= m; ++i) scanf("%d %d %d %d", &arr[i].si, &arr[i].ti, &arr[i].wi, &arr[i].op); 112 Init(2 * m + 3); 113 addedge(0, 2 * m + 1, K, 0); 114 for(int i = 1; i <= m; ++i) addedge(2 * m + 1, 2 * i - 1, 1, 0); 115 for(int i = 1; i <= m; ++i) addedge(2 * i - 1, 2 * i, 1, -arr[i].wi); 116 for(int i = 1; i <= m; ++i) addedge(2 * i, 2 * m + 2, 1, 0); 117 for(int i = 1; i <= m; ++i) 118 { 119 for(int j = 1; j <= m; ++j) 120 { 121 if(i == j) continue; 122 if(arr[i].ti <= arr[j].si) 123 { 124 if(arr[i].op == arr[j].op) 125 { 126 addedge(2 * i, 2 * j - 1, 1, W); 127 } 128 else 129 { 130 addedge(2 * i, 2 * j - 1, 1, 0); 131 } 132 } 133 } 134 } 135 int cost = 0; 136 minCostMaxflow(0, 2 * m + 2, cost); 137 printf("%d\n", -cost); 138 } 139 return 0; 140 }