Codeforces题解集

 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

 

猜你喜欢

转载自www.cnblogs.com/hs-zlq/p/12274138.html