Codeforces题解集

Codeforces 2019年12月19日到 2020年2月12日 的部分比赛题

 

Educational Codeforces Round 82 (Rated for Div. 2)

Fill The Bag

  给出m(≤1e5)个盒子,盒子的大小是2的幂次。可以选择把一个盒子分成大小相同的两部分,问最少执行几次分盒子的操作,可以装满大小为n(≤1e18)的背包。

  把n转化为二进制,代表可以由若干种2的幂次的盒子各一个装满。从小往大贪心地使用已有的盒子,不足时把第一个比它大的盒子分开。

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 #define ll long long
 4 #define inc(i, l, r) for (int i = l; i <= r; i++)
 5 
 6 const int maxn = 1e6 + 5;
 7 
 8 ll t, n, m, a[maxn];
 9 ll num[70];
10 
11 int main() {
12     ios::sync_with_stdio(false);
13     cin.tie(nullptr);
14     cin >> t;
15     while (t--) {
16         cin >> n >> m;
17         ll tot = 0;
18         memset(num, 0, sizeof(num));
19         inc(i, 0, m - 1) {
20             cin >> a[i];
21             tot += a[i];
22             num[(int)log2((double)a[i])]++;
23         }
24         if (tot < n) {
25             cout << "-1\n";
26             continue;
27         }
28         ll ans = 0;
29         for (int i = 0; n; i++) {
30             if (n & 1) {
31                 if (num[i])
32                     num[i]--;
33                 else
34                     inc(j, i + 1, 31) if (num[j]) {
35                         num[j]--;
36                         num[i] += (1 << (j - i)) - 1;
37                         ans += j - i;
38                         break;
39                     }
40             }
41             num[i + 1] += num[i] / 2;
42             n >>= 1;
43         }
44         cout << ans << "\n";
45     }
46 }
View Code

 

Erase Subsequences

  给出两个串s, t,长度≤400,问s的两个不相交的子序列能否拼接成 t.

  枚举 t 的分界处,设左子串为t1,右子串为t2,遍历s,其实就是考虑是放到t1还是t2。dp[i]表示已匹配到t1[i]时,t2最远匹配到t2[dp[i]],当dp[cnt]=size(t)时表示匹配成功。时间复杂度O(n^3)

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 #define ll long long
 4 #define inc(i, l, r) for (int i = l; i <= r; i++)
 5 
 6 const int maxn = 1e3 + 5;
 7 
 8 int c;
 9 char s[maxn], t[maxn];
10 
11 int main() {
12   //  ios::sync_with_stdio(false);
13   //  cin.tie(nullptr);
14     cin >> c;
15     while (c--) {
16         scanf("%s%s", s, t + 1);
17         int l1 = strlen(s), l2 = strlen(t + 1), f = 0;
18         inc(cnt, 1, l2) {
19             vector<int> dp(cnt + 1, -1), nxt(cnt + 1);
20             dp[0] = cnt;
21             inc(i, 0, l1 - 1) {
22                 inc(j, 0, cnt) nxt[j] = dp[j];
23                 inc(j, 0, cnt - 1) if (s[i] == t[j + 1] && dp[j] != -1)
24                     nxt[j + 1] = max(nxt[j + 1], dp[j]);
25                 inc(j, 0,
26                     cnt) if (dp[j] != -1 && dp[j] < l2 && s[i] == t[dp[j] + 1])
27                     nxt[j] = max(nxt[j], dp[j] + 1);
28                 inc(j, 0, cnt) dp[j] = nxt[j];
29             }
30             if (dp[cnt] == l2) {
31                 f = 1;
32                 break;
33             }
34         }
35         if (f)
36             printf("YES\n");
37         else
38             printf("NO\n");
39     }
40 }
View Code

 

 

Codeforces Round #618 (Div. 1)

Anu Has a Function

  定义f(x, y) = x | y - y,给出一个数列,要改变顺序顺序后,使得f(f(x1, x2), x3), …)最大

  这种题目直接考虑01的四种组合结果会是什么。发现只有f(1, 0)结果会是1,所以对于某一数位,只有1 0 0 …是有意义的。找到最高的数位满足这种状况,在确定了第一个数后,后面的数无论是什么顺序都不影响了。

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 #define ll long long
 4 #define inc(i, l, r) for (int i = l; i <= r; i++)
 5 #define dec(i, l, r) for (int i = l; i >= r; i--)
 6 
 7 const int maxn = 1e6 + 5;
 8 
 9 int a[maxn], n, vis[maxn];
10 
11 int main() {
12     ios::sync_with_stdio(false);
13     cin.tie(nullptr);
14     cin >> n;
15     inc(i, 0, n - 1) cin >> a[i];
16     int res = -1;
17     dec(i, 30, 0) {
18         int pos = -1;
19         inc(j, 0, n - 1) if (a[j] >> i & 1) {
20             if (pos == -1)
21                 pos = j;
22             else
23                 pos = -2;
24         }
25         if (pos >= 0) {
26             res = pos;
27             break;
28         }
29     }
30     if (res >= 0) {
31         cout << a[res] << " ";
32         vis[res] = 1;
33     }
34     inc(i, 0, n - 1) if (!vis[i]) cout << a[i] << " ";
35 }
View Code

 

Aerodynamic

  给出一个凸多边形P,我们可以看作一个空心的框,想像(0, 0) 处有一个钉子,框在保证钉子在里面的前提下平移,经过的区域形成一个新的多边形T。问P与T是否相似。

  另一种定义,P中存在向量(x, y),则点(x, y)在T内(上)。

  考虑到存在向量(x,y)就必存在(-x,-y),T必然是中心对称。那么当P是中心对称时,使P中心点在原点,可以发现若(x, y)在P上,则(2x, 2y)在T上;而对于P外的某点,考虑中心对称的且在边上的两点,构成向量(x, y),必不存在该方向上更长的向量(P是凸包)。所以P与T相似,且相似比为1:2。

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 #define ll long long
 4 #define inc(i, l, r) for (int i = l; i <= r; i++)
 5 
 6 const int maxn = 1e6 + 5;
 7 
 8 ll x[maxn], y[maxn];
 9 int n;
10 
11 int main() {
12     ios::sync_with_stdio(false);
13     cin.tie(nullptr);
14     cin >> n;
15     inc(i, 0, n - 1) cin >> x[i] >> y[i];
16     if (n & 1) {
17         printf("no\n");
18         exit(0);
19     }
20     ll cx = x[0] + x[n / 2], cy = y[0] + y[n / 2];
21     inc(i, 1,
22         n / 2 - 1) if (x[i] + x[i + n / 2] != cx || y[i] + y[i + n / 2] != cy) {
23         printf("no\n");
24         exit(0);
25     }
26     printf("yes\n");
27 }
View Code

 

Water Balance

  给出一个数列,可以使一个连续区间的数,都变成该区间的平均数。问经过任意次操作,字典序最小的结果

  考虑我们总是优先使第一个最小化,第一个最小化后,继续检查后面能否更小。维护一个递增的单调栈,出现逆序就合并。

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 #define ll long long
 4 #define inc(i, l, r) for (int i = l; i <= r; i++)
 5 #define dec(i, l, r) for (int i = l; i >= r; i--)
 6 
 7 const int maxn = 1e6 + 5;
 8 
 9 ll a[maxn];
10 int n;
11 
12 struct p {
13     double ave;
14     int num;
15 };
16 stack<p> s;
17 vector<double> res;
18 
19 int main() {
20     ios::sync_with_stdio(false);
21     cin.tie(nullptr);
22     cin >> n;
23     inc(i, 1, n) cin >> a[i];
24     inc(i, 1, n) {
25         double sum = a[i];
26         int num = 1;
27         while (!s.empty() && sum / num < s.top().ave) {
28             sum += s.top().ave * s.top().num;
29             num += s.top().num;
30             s.pop();
31         }
32         s.push({sum / num, num});
33     }
34     while (!s.empty()) {
35         inc(i, 1, s.top().num) res.push_back(s.top().ave);
36         s.pop();
37     }
38     dec(i, n - 1, 0) printf("%.12f ", res[i]);
39 }
View Code

 

 

 Educational Codeforces Round 81 (Rated for Div. 2)

Same GCDs

  给出a和m,1≤a<m≤1e10,问有多少个x,0≤x<m,gcd(a, m)==gcd(a+x, m)

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 #define ll long long
 4 #define inc(i, l, r) for (int i = l; i <= r; i++)
 5 
 6 int t;
 7 ll a, m;
 8 
 9 const int maxnum = 1e5;
10 int prim[maxnum], pvis[maxnum + 5], pcnt;
11 void getprim() {
12     for (int i = 2; i <= maxnum; i++) {
13         if (!pvis[i]) prim[++pcnt] = i;
14         for (int j = 1; j <= pcnt && prim[j] * i <= maxnum; j++) {
15             pvis[prim[j] * i] = 1;
16             if (i % prim[j] == 0) break;
17         }
18     }
19 }
20 
21 ll cal(ll x) {
22     ll org = x;
23     for (int i = 1; i <= pcnt && x > 1; i++) {
24         if (x % prim[i] == 0) org = org / prim[i] * (prim[i] - 1);
25         while (x % prim[i] == 0) x /= prim[i];
26     }
27     if (x > 1) org = org / x * (x - 1);
28     return org;
29 }
30 
31 int main() {
32     getprim();
33     cin >> t;
34     while (t--) {
35         cin >> a >> m;
36         cout << cal(m / __gcd(a, m)) << "\n";
37     }
38 }
View Code

 

Permutation Separation

  给出一个n的排列,2≤n≤2e5,每个数有权值ai,现要分成两个非空子串,并对应两个数字集合,通过付出每个数字的权值的代价,可以把一个数字从原来的集合移动到另一个集合里。要使左子串对应的数字集合里的所有数都小于右字串对应的集合,或使若某一集合为空集,问最小代价

  易知最终左子串对应的集合必然是1,2,,,n的一个前缀,扫描一遍,维护一个“变成前缀为i的集合需要的代价”的线段树

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 #define ll long long
 4 #define inc(i, l, r) for (int i = l; i <= r; i++)
 5 
 6 const int maxn = 2e5 + 5;
 7 
 8 int n, p[maxn], a[maxn];
 9 int val[maxn];
10 ll fv;
11 
12 ll res, tmp;
13 
14 ll w[4 * maxn], f[4 * maxn];
15 
16 void build(int k, int l, int r) {
17     if (l == r) {
18         fv += val[l];
19         w[k] = fv;
20         return;
21     }
22     int m = (l + r) / 2;
23     build(2 * k, l, m);
24     build(2 * k + 1, m + 1, r);
25     w[k] = min(w[2 * k], w[2 * k + 1]);
26 }
27 
28 void down(int k, int l, int r) {
29     f[2 * k] += f[k];
30     f[2 * k + 1] += f[k];
31     w[2 * k] += f[k];
32     w[2 * k + 1] += f[k];
33     f[k] = 0;
34 }
35 
36 void change(int k, int l, int r, int a, int b, int val) {
37     if (a <= l && r <= b) {
38         w[k] += val;
39         f[k] += val;
40         return;
41     }
42     if (f[k]) down(k, l, r);
43     int m = (l + r) / 2;
44     if (a <= m) change(2 * k, l, m, a, b, val);
45     if (b > m) change(2 * k + 1, m + 1, r, a, b, val);
46     w[k] = min(w[2 * k], w[2 * k + 1]);
47 }
48 
49 int main() {
50     cin >> n;
51     inc(i, 1, n) cin >> p[i];
52     inc(i, 1, n) {
53         cin >> a[i];
54         val[p[i]] = a[i];
55     }
56     build(1, 0, n);
57     res = a[1];
58     for (int i = 1; i < n; i++) {
59         change(1, 0, n, 0, p[i] - 1, a[i]);
60         change(1, 0, n, p[i], n, -a[i]);
61         res = min(res, w[1]);
62     }
63     cout << res;
64 }
View Code

 

 

Educational Codeforces Round 80 (Rated for Div. 2)

Minimax Problem

  给出N×M的矩阵,N≤3e5,M≤8。现取出其中两行,“合并”成新的一行。所谓合并,是在相同位置的数取最大值。现在要最大化该数列的最小值,输出选取的两行行号

  比较容易想到是二分答案。judge时要让每一个位置至少有一个数大于ans,可以把大于ans的数位hash成1,然后在至多2**M的hash数组里寻找是否有满足条件的两列。这里降低时间复杂度是利用了2**M<N的特点。注意二分的时候,避免没有judge为true(即ans实际为0,却不会运行到一步)的情况

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 #define ll long long
 4 #define inc(i, l, r) for (int i = l; i <= r; i++)
 5 #define pii pair<int, int>
 6 #define fi first
 7 #define se second
 8 #define pb push_back
 9 
10 const int maxn = 3e5 + 5;
11 
12 int a[maxn][10], n, m, r1, r2;
13 int h[300];
14 
15 inline bool judge(int ans) {
16     memset(h, 0, sizeof(h));
17     inc(i, 0, n - 1) {
18         int val = 0;
19         inc(j, 0, m - 1) {
20             if (a[i][j] >= ans) val += 1 << j;
21         }
22         h[val] = i + 1;
23     }
24     int sz = (1 << m) - 1;
25     inc(i, 0, sz) {
26         inc(j, 0, sz) if ((i | j) == sz && h[i] && h[j]) {
27             r1 = h[i], r2 = h[j];
28             return true;
29         }
30     }
31     return false;
32 }
33 
34 int main() {
35     scanf("%d %d", &n, &m);
36     inc(i, 0, n - 1) inc(j, 0, m - 1) scanf("%d", &a[i][j]);
37     int l = 0, r = (int)1e9 + 1, ans = -1;
38     while (l + 1 < r) {
39         int m = (r - l) / 2 + l;
40         if (judge(m))
41             l = m, ans = m;
42         else
43             r = m;
44     }
45     if (ans == -1)
46         printf("1 1\n");
47     else
48         cout << r1 << " " << r2;
49 }
View Code

 

Messenger Simulator 

  初始为1,2,,,n的一个排列,代表n位好友的消息列表。给出m条消息,其中每一条都会使对应的好友消息在列表中置顶,即排列中的该数字被提前到第一个位置。问每个人在消息列表中最靠前和最靠后的位置。

  考虑到一个人的位置只有在发消息时会减少,否则只会递增。所以,只关心在发消息时以及结束时刻他前面有多少人。建立一个n+m的BIT,初始时后n个位置置为1,代表了这n个人;每次处理一位好友消息时,查询前缀和,更新最大值,把他置于所有人前一位,并删去原来所在位置.

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 #define ll long long
 4 #define inc(i, l, r) for (int i = l; i <= r; i++)
 5 #define lowbit(x) x&(-x)
 6 
 7 const int maxn = 6e5 + 5;
 8 
 9 int n, m, q;
10 
11 int f[maxn];
12 void add(int x, int val) {
13     for (; x <= n + m; x += lowbit(x)) f[x] += val;
14 }
15 int get(int x) {
16     int res = 0;
17     for (; x; x -= lowbit(x)) res += f[x];
18     return res;
19 }
20 
21 int a[maxn], b[maxn], pos[maxn];
22 
23 int main() {
24     cin >> n >> m;
25     inc(i, 1, n) {
26         a[i] = b[i] = i;
27         pos[i] = m + i;
28         add(i + m, 1);
29     }
30     inc(i, 1, m) {
31         cin >> q;
32         a[q] = 1;
33         b[q] = max(b[q], get(pos[q] - 1) + 1);
34         add(pos[q], -1);
35         pos[q] = m - i + 1;
36         add(m - i + 1, 1);
37     }
38     inc(i, 1, n) b[i] = max(b[i], get(pos[i] - 1) + 1);
39     inc(i, 1, n) printf("%d %d\n", a[i], b[i]);
40 }
View Code

  

 

Codeforces Round #610 (Div. 2)

Petya and Exam

  有一次测验,有N道题,考试时间T,已知每一题是容易题还是困难题,解决前者需要时间a,后者需要b,每道题有个ti,考生在离开考场(可以选择提前离场)时若不小于该时间而未解决该题,他总分就是0分。问考生最多能获得多少分(解决一题得一分)。

  思维点在于考虑到每个ti的前一秒是离场的最佳时间,检查能否把必须做的题了,外加做额外的题,更新答案。另外考试结束时也要检查是否可以更新答案。

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 #define ll long long
 4 #define inc(i, l, r) for (int i = l; i <= r; i++)
 5 
 6 const int maxn = 2e5 + 5;
 7 
 8 int m, n, t, a, b;
 9 
10 struct problem {
11     int hard, gg;
12     bool operator<(const problem& o) const { return gg < o.gg; }
13 } p[maxn];
14 
15 int main() {
16     ios::sync_with_stdio(false);
17     cin.tie(nullptr);
18     cin >> m;
19     while (m--) {
20         cin >> n >> t >> a >> b;
21         ll ea = 0, eb = 0;
22         inc(i, 0, n - 1) {
23             cin >> p[i].hard;
24             ea += p[i].hard == 0;
25             eb += p[i].hard == 1;
26         }
27         inc(i, 0, n - 1) cin >> p[i].gg;
28         sort(p, p + n);
29         p[n] = {-1, t + 1};
30         ll task = 0;
31         ll na = 0, nb = 0, res = 0;
32         for (int i = 0, j; i <= n; i = j) {
33             if (p[i].gg - 1 >= task) {
34                 ll tot = na + nb, tmp = p[i].gg - 1 - task;
35                 if (tmp <= (ea - na) * a)
36                     tot += tmp / a;
37                 else {
38                     tot += ea - na;
39                     tmp -= (ea - na) * a;
40                     if (tmp <= (eb - nb) * b)
41                         tot += tmp / b;
42                     else
43                         tot += eb - nb;
44                 }
45                 res = max(res, tot);
46             }
47             j = i;
48             while (j <= n && p[j].gg == p[i].gg) {
49                 if (p[j].hard == 0)
50                     task += a, na++;
51                 else
52                     task += b, nb++;
53                 j++;
54             }
55         }
56         cout << res << "\n";
57     }
58 }
View Code

 

Enchanted Artifact

  交互题。定义Edit distance是串s经过增,删,改(每次一个字符)变成串t的次数。现有一个串s(size≤300),用户询问串t(size≤300),返回Edit distance,最多询问s.size+2次(包括最终返回0的“询问”)

  先询问300个a的串和300个b的串得到s有几个a和几个b,依据:由s变成全a只能把b改成a以及添加a。此时,我们也知道了如果询问一个与s长度相同全为b的串(记作tt)会得到的答案。然后询问这样的串t,长度与s相同,除了某一位置为a,其他都是b,可以推理出,a的位置与s匹配与 询问tt的返回值-1 是等价的。又注意到最后一个位置可以通过计算a的个数得出,所以无须询问,总询问次数刚好size+2次。

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 #define inc(i, l, r) for (int i = l; i <= r; i++)
 4 
 5 int a, b, x, n;
 6 
 7 void read() {
 8     cout << '\n';
 9     fflush(stdout);
10     cin >> x;
11     if (x == 0) exit(0);
12 }
13 
14 int main() {
15     inc(i, 1, 300) cout << 'a';
16     read();
17     a = 300 - x;
18 
19     inc(i, 1, 300) cout << 'b';
20     read();
21     b = 300 - x;
22 
23     n = a + b;
24     string res(n, 'b');
25 
26     int ta = 0;
27     inc(i, 0, n - 2) {
28         inc(j, 0, n - 1) if (i == j) cout << "a";
29         else cout << "b";
30         read();
31         if (x == a - 1) {
32             res[i] = 'a';
33             ta++;
34         }
35     }
36     if (ta != a) res[n - 1] = 'a';
37     cout << res;
38     read();
39 }
View Code

 

 

Codeforces Round #607 (Div. 1)

Beingawesomeism

  给出r×c的格子,上面有字符A和P。选择一个1×X的区域,指定某一方向与步长,该方向需与指定区域垂直;该方向的格子会被与起始指定区域同一行(列)的字符同化。问最少经过几次操作全部变成A

  模拟题。最多4步(或者不能),可以先判断能否3步的,其次2步,最后1步,节省代码量。

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 #define ll long long
 4 #define inc(i, l, r) for (int i = l; i <= r; i++)
 5 
 6 char a[65][65];
 7 int t, w, h;
 8 
 9 bool judge(int x, int d) {
10     if (d == 0) {
11         inc(i, 0, w - 1) if (a[x][i] != 'A') return 0;
12     } else {
13         inc(i, 0, h - 1) if (a[i][x] != 'A') return 0;
14     }
15     return 1;
16 }
17 
18 int main() {
19     ios::sync_with_stdio(false);
20     cin.tie(nullptr);
21     cin >> t;
22     while (t--) {
23         cin >> h >> w;
24         int all = 0;
25         inc(i, 0, h - 1) {
26             cin >> a[i];
27             inc(j, 0, w - 1) all += a[i][j] == 'A';
28         }
29         if (all == 0)
30             printf("MORTAL\n");
31         else if (all == h * w)
32             printf("0\n");
33         else {
34             int res = 4;
35             inc(i, 1, h - 2) if (a[i][0] == 'A' || a[i][w - 1] == 'A') res = 3;
36             inc(i, 1, w - 2) if (a[0][i] == 'A' || a[h - 1][i] == 'A') res = 3;
37             inc(i, 1, h - 2) if (judge(i, 0)) res = 2;
38             inc(i, 1, w - 2) if (judge(i, 1)) res = 2;
39             if (a[0][0] == 'A' || a[0][w - 1] == 'A' || a[h - 1][0] == 'A' ||
40                 a[h - 1][w - 1] == 'A')
41                 res = 2;
42             if (judge(0, 0) || judge(h - 1, 0) || judge(0, 1) ||
43                 judge(w - 1, 1))
44                 res = 1;
45             printf("%d\n", res);
46         }
47     }
48 }
View Code

 

Jeremy Bearimy

  给出一个2n个点的树,两两匹配树上的所有节点,匹配后这些节点对的距离总和,问该距离总和的最小值与最大值。

  考虑以某一条边为界,把树分为两部分。要最大化距离总和时,要尽可能匹配不同部分的点,那么这条边就被计算min(sz, 2n-sz)次;,sz为一部分的节点数;要最小化距离总和时,要尽可能匹配同一部分的点,那么这条边就被计算 sz & 1 次。

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 #define ll long long
 4 #define inc(i, l, r) for (int i = l; i <= r; i++)
 5 #define pii pair<int, int>
 6 #define fi first
 7 #define se second
 8 #define pb push_back
 9 
10 const int maxn = 2e5 + 5;
11 
12 vector<pii> g[maxn];
13 
14 int t, n, u, v, dis;
15 ll res1, res2;
16 
17 int dfs(int x, int par) {
18     int sz = 1;
19     for (int i = 0; i < g[x].size(); i++) {
20         if (g[x][i].fi != par) {
21             int t = dfs(g[x][i].fi, x);
22             res1 += t % 2 * g[x][i].se;
23             res2 += (ll)min(t, 2 * n - t) * g[x][i].se;
24             sz += t;
25         }
26     }
27     return sz;
28 }
29 
30 int main() {
31     ios::sync_with_stdio(false);
32     cin.tie(nullptr);
33     cin >> t;
34     while (t--) {
35         cin >> n;
36         inc(i, 1, 2 * n) g[i].clear();
37         inc(i, 1, 2 * n - 1) {
38             cin >> u >> v >> dis;
39             g[u].pb(pii(v, dis));
40             g[v].pb(pii(u, dis));
41         }
42         res1 = res2 = 0;
43         dfs(1, -1);
44         printf("%lld %lld\n", res1, res2);
45     }
46 }
View Code

 

 

Educational Codeforces Round 78 (Rated for Div. 2)

Segment Tree

  给出n个区间,这些区间的端点取遍1-2n。当两个区间相交时(不能包含),它们代表的节点之间就有一条边。判断依此生成的图是否是树。

  对所有节点排序,遇到左端点加入set,遇到右端点时统计与其左端点间有多少个点,并删除左端点。用并查集维护集合关系。最后根据边数和连通性判断是否是树。因为每次都是遍历寻找左右端点间的点,此时算法的复杂度是O(n^2)。当统计点的个数大于n-1时,一定不是树,就可以break出来,此时是O(nlogn)。

 

Tests for problem D

  给出n个节点的树,要为每一个节点构造一个区间,使得区间的左右端点取遍1-2n,两个节点之间是否有边等价于它们的区间是否相交(不能包含)。

  根的左端点为1,先序遍历,维护当前已使用的数字,当前区间的右端点贪心地取最小值——即右端点与已使用的数字之间的空余刚好能容下它的儿子的左端点(它的儿子的左端点就其次取这些空余的数字)。并且儿子的左端点越大越先遍历,这样可以保证儿子之间不会相交。

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 #define ll long long
 4 #define inc(i, l, r) for (int i = l; i <= r; i++)
 5 #define pii pair<int, int>
 6 #define fi first
 7 #define se second
 8 #define pb push_back
 9 
10 const int maxn = 1e6 + 5;
11 
12 vector<int> g[maxn];
13 int n, u, v;
14 pii res[maxn];
15 
16 int top = 1, root = 1;
17 void dfs(int x, int par) {
18     top += g[x].size();
19     if (x == root) top++;
20     res[x].se = top;
21     for (int i = 0, j = 0; i < g[x].size(); i++) {
22         if (g[x][i] != par) {
23             res[g[x][i]].fi = top - ++j;
24         }
25     }
26     for (int i = 0; i < g[x].size(); i++) {
27         if (g[x][i] != par) {
28             dfs(g[x][i], x);
29         }
30     }
31 }
32 
33 int main() {
34     ios::sync_with_stdio(false);
35     cin.tie(nullptr);
36     cin >> n;
37     inc(i, 1, n - 1) {
38         cin >> u >> v;
39         g[u].pb(v);
40         g[v].pb(u);
41     }
42     dfs(1, -1);
43     res[1].fi = 1;
44     inc(i, 1, n) cout << res[i].fi << " " << res[i].se << "\n";
45 }
View Code

 

posted @ 2020-02-07 19:03  Linqi05  阅读(224)  评论(0编辑  收藏  举报