Codeforces Round #359 (Div. 2)

5/5

这场CF因为java课设没打,于是去VJ开了一场模拟补了下,结果打了1个小时就累了,于是滚去睡中午觉2333,补完题写个题解。这个博客基本就是记录我ACM做过什么题(不完全)以及对一些算法的理解~~算法专题以后有时间就补补。我发现CF的题目还是不是很难的(div2),看着题解慢慢理解还是能够跟得上的,打了一年多终于发现自己有点用了(/▽╲)!

 

题A Free Ice Cream

题意:给你n个操作,一开始有x个物品,然后+代表入货,-代表卖出,如果不够卖出则不卖给他,最后问你剩下的物品和没卖给的人数。

题解:直接模拟。

 

 1 /*zhen hao*/
 2 #include <bits/stdc++.h>
 3 using namespace std;
 4 
 5 typedef long long ll;
 6 
 7 int main() {
 8 //  freopen("case.in", "r", stdin);
 9   int n, res = 0;
10   ll d, t;
11   cin >> n >> d;
12   for (int i = 0; i < n; i++) {
13     char c;
14     cin >> c >> t;
15     if (c == '+') d += t;
16     else {
17       if (d >= t) d -= t;
18       else res++;
19     }
20   }
21   cout << d << ' ' << res << endl;
22 }
代码君

 

题B Little Robber Girl's Zoo

题意:给你一个序列,然后你可以制定一个偶数长度的区间让其两两交换,也就是i和i+1,i+2和i+3……,最后得到一个递增的序列,然你输出操作,要求操作不超过2e4次。

题解:注意到序列的个数是100,那么我们利用冒泡排序,如果要交换那就push进答案去,最后输出来的都是长度为2的区间即可,这样的区间最多是n * (n - 1) / 2个,所以不用担心出错。

 

 1 /*zhen hao*/
 2 #include <bits/stdc++.h>
 3 using namespace std;
 4 
 5 #define lson l, m, rt*2
 6 #define rson m + 1, r, rt*2+1
 7 #define xx first
 8 #define yy second
 9 
10 typedef pair<int,int> pii;
11 typedef long long ll;
12 typedef unsigned long long ull;
13 
14 const int maxn = 1e2 + 10;
15 int a[maxn], n;
16 vector<pii> v;
17 
18 int main() {
19 //  freopen("case.in", "r", stdin);
20   cin >> n;
21   for (int i = 0; i < n; i++) cin >> a[i];
22   for (int i = 0; i < n - 1; i++) {
23     bool flag = false;
24     for (int j = 0; j < n - 1 - i; j++) {
25       if (a[j] > a[j + 1]) {
26         swap(a[j], a[j + 1]);
27         v.push_back(make_pair(j + 1, j + 2));
28         flag = true;
29       }
30     }
31     if (!flag) break;
32   }
33   for (int i = 0; i < (int)v.size(); i++)
34     cout << v[i].xx << ' ' << v[i].yy << endl;
35   return 0;
36 }
代码君

 

题C Robbers' watch

题意:给你n和m表示x:y,其中0<=x<n,0<=y<m,然后问你表示为7进制要均有不同的数字,如果一个数不够这个位数的话要加前导零,问你有多少种可能?

题解:显然如果n + m的位数大于8的话是不可能的,所以这样可以暴力处理,对于那些满足的进行暴力枚举即可。

 

 1 /*zhen hao*/
 2 #include <bits/stdc++.h>
 3 using namespace std;
 4 
 5 #define lson l, m, rt*2
 6 #define rson m + 1, r, rt*2+1
 7 #define xx first
 8 #define yy second
 9 
10 typedef pair<int,int> pii;
11 typedef long long ll;
12 typedef unsigned long long ull;
13 
14 const int maxn = 300;
15 int dp1[maxn], dp2[maxn];
16 
17 bool check(int x, int& l) {
18   if (x == 0) return l = 1;
19   l = 0;
20   while (x) {
21     l++;
22     x /= 7;
23   }
24   return l <= 7;
25 }
26 
27 void pre(int n, int l, int* dp) {
28   for (int i = 0; i < n; i++) {
29     int s = 0, t = i, ok = 1;
30     for (int j = 0; j < l; j++) {
31       if (s & 1 << (t % 7)) { ok = 0; break; }
32       s ^= (1 << (t % 7));
33       t /= 7;
34     }
35     if (ok) dp[s]++;
36 //    if (ok) cout << s << endl;
37   }
38 }
39 
40 int main() {
41 //  freopen("case.in", "r", stdin);
42   int n, m, l1, l2;
43   cin >> n >> m;
44   if (!check(n - 1, l1) || !check(m - 1, l2)) ;
45   if (l1 + l2 > 7) {
46      puts("0"); return 0;
47   }
48   pre(n, l1, dp1);
49   pre(m, l2, dp2);
50   ll res = 0;
51   for (int i = 1; i < 1 << 7; i++)
52     for (int j = 1; j < 1 << 7; j++)
53       if (!(i & j)) res += 1ll * dp1[i] * dp2[j];
54   cout << res << endl;
55 }
代码君

 

题D Kay and Snowflake

题意:给你一颗以1为根的树,然后有q个询问,问你以这个顶点为根的子树的重心是什么?

题解:这道题根本不会做,主要考虑的是树的重心的一些性质。

先丢出一个博文:戳这里

这里讲到树的重心的三个性质,以后再慢慢研究,这里用到的是第二条性质:

当合并两颗树的时候新的树的重心就是原来两棵树的重心的路径上的一个点。

应用到这道题中,首先要知道对于一个点u的重心一定是在他的重儿子上(重儿子就是子树上最多结点的一个儿子节点)。

为什么呢?

先给出树的重心的其中一个定义:重心是所有点中那个点的所有子树的最大结点最小的一个点。现在我们证明的是一个点的重心一定是它本身或者是它的重儿子所在子树上的一个点,假设这个是不对的,也就是可能在别的非重儿子的子树上,现在假设重儿子结点数是x,这个非重儿子的结点数是y,那么有x > y,然后如果是非重儿子子树上的一个点,那么他必有的一个连通块的个数是x + 1(根节点),然后如果选的是本身,那么最大的结点数是x(重儿子子树的个数),所以不可能在轻儿子子树上。

接着回到这道题,已知是本身或者在重儿子上,那么对于一棵树的所有子树中,只需要考虑重儿子的子树,假设这个子树的重心是v,那么v到根节点的路径上的所有点都可能是重心(根据性质二可知),所以我们只要dfs一次即可,复杂度O(n + q)。

 1 /*zhen hao*/
 2 #include <bits/stdc++.h>
 3 using namespace std;
 4 
 5 const int maxn = 3e5 + 10;
 6 int e, head[maxn], res[maxn], sz[maxn], fa[maxn];
 7 
 8 struct Edge {
 9   int v, nx;
10 } edges[maxn * 2];
11 
12 void init() {
13   e = 0;
14   memset(head, -1, sizeof head);
15 }
16 
17 void add_edge(int u, int v) {
18   edges[e] = (Edge){v, head[u]};
19   head[u] = e++;
20 }
21 
22 void dfs(int u) {
23   res[u] = u;
24   sz[u] = 1;
25   int w_son = -1;
26   for (int i = head[u]; ~i; i = edges[i].nx) {
27     int v = edges[i].v;
28     dfs(v);
29     sz[u] += sz[v];
30     if (w_son == -1 || sz[v] > sz[w_son])
31       w_son = v;
32   }
33   if (w_son != -1 && sz[w_son] > sz[u] / 2) {
34     int v = res[w_son];
35     while (sz[u] - sz[v] > sz[u] / 2) v = fa[v];
36     res[u] = v;
37   }
38 }
39 
40 int main() {
41 //  freopen("case.in", "r", stdin);
42   int n, q;
43   cin >> n >> q;
44   init();
45   for (int i = 1; i < n; i++) {
46     scanf("%d", &fa[i]);
47     fa[i]--;
48     add_edge(fa[i], i);
49   }
50   dfs(0);
51   for (int i = 0; i < q; i++) {
52     int x;
53     scanf("%d", &x);
54     --x;
55     printf("%d
", res[x] + 1);
56   }
57 }
代码君

 

题E Optimal Point

题意:给你n个点(xi,yi,zi),让你找一个点(X,Y,Z)使得max{|x1 - X| + |y1 - Y| + |z1 - Z|}最小,让你输出任意一个满足的点,还有就是这个X、Y、Z必须是整数。

题解:最大最小问题显然要二分,怎么二分呢?在下实在渣渣,参考了叉姐的题解,还要研究上半天才写得出代码,果然是渣渣┑( ̄Д  ̄)┍,先丢出叉姐的解题报告:戳这里

接下来我将重复阐述一样的解法,当然会加上一点点我的理解:

首先是二分答案l,

然后对于所有点i均有

|xi - X| + |yi - Y| + |zi - Z| <= l

然后绝对值可以拆掉写成:

|xi - X| = max{ xi - X, X - xi }

(同理还有两个)

然后上述的式子就变成:

|xi - X| + |yi - Y| + |zi - Z| = max{ xi + yi + zi - (X + Y + Z), -xi + yi + zi - (-X + Y + Z)(同理还有六个) } <= l

于是单独写出来就有:

max{xi + yi + zi} - (X + Y + Z) <= l

X + Y + Z - min{xi + yi + zi} <= l

=>  max{xi + yi + zi} -l <= X + Y + Z <= min{xi + yi + zi} + l

同理有如下四个:

2) max{-xi + yi + zi} -l <= -X + Y + Z <= min{-xi + yi + zi} + l

3)max{xi + yi - zi} -l <= X + Y - Z <= min{xi + yi - zi} + l

4)max{-xi + yi - zi} -l <= -X + Y - Z <= min{-xi + yi - zi} + l

然后就是变形的时间了,另S = Y + Z,D = Y - Z,然后我们把前两个表示为(引用一下):

接下来讲述一下怎么解出这个X的范围?

假设有两个区间[a,b]和[c,d],然后他们相交,问你满足什么条件?

考虑其对立问题:不相交。

如果[a,b]在左,那么要满足b < c;否则要满足d < a,也就是b < c || d < a

取反得c <= d && a <= d,所以这样就得到了两个X的范围(四个不等式),然后取并集之后得到[x3,y3],然后就是枚举X存不存在满足条件的Y和Z。怎样得到Y和Z呢?已知S和D的范围,然后因为

S = Y + Z; D = Y - Z;

因此Y = (S + D) / 2; Z = (S - D) / 2;

但是因为Y和Z都是整数,因此需要S和D同奇偶,所以判定一下就好,有的话就返回yes表示二分的结果是对的,可以继续缩小枚举的len。至此,这道题就解完了。。。。

 1 /*zhen hao*/
 2 #include <bits/stdc++.h>
 3 using namespace std;
 4 
 5 #define lson l, m, rt*2
 6 #define rson m + 1, r, rt*2+1
 7 #define xx first
 8 #define yy second
 9 
10 typedef pair<int,int> pii;
11 typedef long long ll;
12 typedef unsigned long long ull;
13 
14 const ll inf = 3 * 1e18;
15 ll mi[5], ma[5], low[5], high[5];
16 ll resx, resy, resz;
17 
18 ll div2(ll x, ll y) {
19   if (x > 0 && y > 0 || x < 0 && y < 0) {
20     ll f = 0;
21     if (x % 2 && y % 2) f = 1;
22     if (x > 0 && y > 0) return x / 2 + y / 2 + f;
23     else return x / 2 + y / 2 - f;
24   }
25   else
26     return (x + y) / 2;
27 }
28 
29 void update(ll s, ll d) {
30   resy = div2(s, d);
31   resz = div2(s, -d);
32 }
33 
34 bool check(ll x) {
35   for (int i = 1; i <= 4; i++) {
36     low[i] = ma[i] - x;
37     high[i] = mi[i] + x;
38     if (low[i] > high[i]) return false;
39   }
40   ll x1 = div2(low[1], -high[2]);
41   ll y1 = div2(high[1], -low[2]);
42   ll x2 = div2(low[3], -high[4]);
43   ll y2 = div2(high[3], -low[4]);
44   ll x3 = max(x1, x2);
45   ll y3 = min(y1, y2);
46   if (x3 > y3) return false;
47   for (ll i = x3; i <= y3; i++) {
48     resx = i;
49     ll s1 = max(low[1] - i, low[2] + i);
50     ll s2 = min(high[1] - i, high[2] + i);
51     ll d1 = max(low[3] - i, low[4] + i);
52     ll d2 = min(high[3] - i, high[4] + i);
53     if (s1 > s2 || d1 > d2) continue;
54     if (s1 < s2) {
55       if ((s1 & 1) == (d1 & 1)) update(s1, d1);
56       else update(s1 + 1, d1);
57       return true;
58     }
59     else if (d1 < d2) {
60       if ((s1 & 1) == (d1 & 1)) update(s1, d1);
61       else update(s1, d1 + 1);
62       return true;
63     } else {
64       if ((s1 & 1) == (d1 & 1)) { update(s1, d1); return true; }
65     }
66   }
67   return false;
68 }
69 
70 int main() {
71 //  freopen("case.in", "r", stdin);
72   int T;
73   cin >> T;
74   while (T--) {
75     int n;
76     scanf("%d", &n);
77     for (int i = 1; i <= 4; i++) {
78       mi[i] = inf; ma[i] = -inf;
79     }
80     for (int i = 0; i < n; i++) {
81       ll x, y, z;
82       scanf("%I64d%I64d%I64d", &x, &y, &z);
83       mi[1] = min(mi[1], x + y + z);  ma[1] = max(ma[1], x + y + z);
84       mi[2] = min(mi[2], -x + y + z); ma[2] = max(ma[2], -x + y + z);
85       mi[3] = min(mi[3], x + y - z);  ma[3] = max(ma[3], x + y - z);
86       mi[4] = min(mi[4], -x + y - z); ma[4] = max(ma[4], -x + y - z);
87     }
88     ll L = -1, R = inf;
89     while (R - L > 1) {
90       ll M = (L + R) >> 1;
91       if (check(M)) R = M;
92       else L = M;
93     }
94     check(R);
95     printf("%I64d %I64d %I64d
", resx, resy, resz);
96 //    cout << resx << ' ' << resy << ' ' << resz << endl;
97   }
98   return 0;
99 }
代码君

 

原文地址:https://www.cnblogs.com/zhenhao1/p/5638927.html