Educational Codeforces Round 81 (Rated for Div. 2)
给出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 }
给出一个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 }
Educational Codeforces Round 80 (Rated for Div. 2)
给出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 }
初始为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 }