Comet OJ Contest #3

好久之前打的,今晚才有空补题。不得不说这个oj的contest质量很好,比赛时间也非常友善。

题目链接:https://cometoj.com/contest/38/problems


A:

温暖签到。multiset就秒了。

 1 /* basic header */
 2 #include <bits/stdc++.h>
 3 /* define */
 4 #define ll long long
 5 #define dou double
 6 #define pb emplace_back
 7 #define mp make_pair
 8 #define sot(a,b) sort(a+1,a+1+b)
 9 #define rep1(i,a,b) for(int i=a;i<=b;++i)
10 #define rep0(i,a,b) for(int i=a;i<b;++i)
11 #define eps 1e-8
12 #define int_inf 0x3f3f3f3f
13 #define ll_inf 0x7f7f7f7f7f7f7f7f
14 #define lson curPos<<1
15 #define rson curPos<<1|1
16 /* namespace */
17 using namespace std;
18 /* header end */
19 
20 const int maxn = 5e2 + 10;
21 multiset<ll>s, a;
22 ll sum = 0;
23 int n, k;
24 
25 int main()
26 {
27     s.clear(); a.clear();
28     scanf("%d%d", &n, &k);
29     rep1(i, 1, n)
30     {
31         ll x; scanf("%lld", &x);
32         for (auto i : a) s.insert(i + x);
33         a.insert(x);
34     }
35     int cnt = 0, total = n * (n - 1) / 2 - k;
36     for (auto i : s)
37     {
38         cnt++; if (cnt <= total) continue;
39         sum += i;
40     }
41     printf("%lld\n", sum);
42     return 0;
43 }
View Code

B:

题⽬意思就是说通过把0变成1来将所有1变成⼀个联通块,要求最少的变换个数。

状压dp,不妨遍历所有列,对于任意含有1的列,情况只有三种:上0下1、上1下0、上1下1,只有这三种情况才需要变化前面列的某些0来使得所有1联通。考察这三种情况。上1下1时,变换个数就是上一个含1列的编号与当前列编号之差的绝对值-1;上1下0时,需要考察上一个含1列的情况,如果上一个含1列的1与当前列的1处于同一行,变换个数就是上一个含1列的编号与当前列编号之差的绝对值-1,反之则为上一个含1列的编号与当前列编号之差的绝对值;上0下1的解法与上1下0的解法类似。

因此,关键点在于保存上一次含1列的情况,当处理到新的含1列时考虑上述情况并更新答案即可。

 1 /* basic header */
 2 #include <bits/stdc++.h>
 3 /* define */
 4 #define ll long long
 5 #define dou double
 6 #define pb emplace_back
 7 #define mp make_pair
 8 #define sot(a,b) sort(a+1,a+1+b)
 9 #define rep1(i,a,b) for(int i=a;i<=b;++i)
10 #define rep0(i,a,b) for(int i=a;i<b;++i)
11 #define eps 1e-8
12 #define int_inf 0x3f3f3f3f
13 #define ll_inf 0x7f7f7f7f7f7f7f7f
14 #define lson curPos<<1
15 #define rson curPos<<1|1
16 /* namespace */
17 using namespace std;
18 /* header end */
19 
20 const int maxn = 1e5 + 10;
21 int n, ans = 0, start = 0, a[2][maxn];
22 int pre, flag = 0, lastPos; //pre: 1(up 1 down 0) 2(up 0 down 1) 3(both 1)
23 
24 int main()
25 {
26     scanf("%d", &n);
27     for (int i = 0; i < 2; i++)
28         for (int j = 0; j < n; j++)
29             scanf("%d", &a[i][j]);
30     for (int j = 0; j < n; j++)
31     {
32         int nowone = 0, nowtwo = 0;
33         if (a[0][j] == 1) nowone = 1;
34         if (a[1][j] == 1) nowtwo = 1;
35         if (nowone || nowtwo)
36         {
37             if (!flag)
38             {
39                 lastPos = j;
40                 if (nowone) pre = 1;
41                 if (nowtwo) pre = 2;
42                 if (nowone && nowtwo) pre = 3;
43                 flag = 1;
44                 continue;
45             }
46             else
47             {
48                 ans += j - lastPos - 1;
49                 if (nowone && nowtwo)
50                 {
51                     pre = 3;
52                 }
53                 else if (nowone && !nowtwo)
54                 {
55                     if (pre == 2)
56                     {
57                         ans++;
58                         pre = 3;
59                     }
60                     else pre = 1;
61                 }
62                 else if (!nowone && nowtwo)
63                 {
64                     if (pre == 1)
65                     {
66                         ans++;
67                         pre = 3;
68                     }
69                     else pre = 2;
70                 }
71                 lastPos = j;
72             }
73         }
74     }
75     printf("%d\n", ans);
76     return 0;
77 }
View Code

C:

子序列中一个数的贡献是2^l,当l超过logm时无意义(因为只要考虑其是否为m的倍数)。设f[i][j][k]为前i个数选了模m为j的k个数的方案数,直接暴力,复杂度O(nmlogm)。

 1 /* basic header */
 2 #include <bits/stdc++.h>
 3 /* define */
 4 #define ll long long
 5 #define dou double
 6 #define pb emplace_back
 7 #define mp make_pair
 8 #define sot(a,b) sort(a+1,a+1+b)
 9 #define rep1(i,a,b) for(int i=a;i<=b;++i)
10 #define rep0(i,a,b) for(int i=a;i<b;++i)
11 #define eps 1e-8
12 #define int_inf 0x3f3f3f3f
13 #define ll_inf 0x7f7f7f7f7f7f7f7f
14 #define lson curPos<<1
15 #define rson curPos<<1|1
16 /* namespace */
17 using namespace std;
18 /* header end */
19 
20 const int maxn = 5e3 + 10, mod = 1e9 + 7;
21 int n, m, a[maxn], f[15][maxn], g[maxn], t = 0, ans = 0;
22 
23 void add_mod(int &x, int y)
24 {
25     x += y;
26     x -= (x >= mod) ? mod : 0;
27 }
28 
29 int mi(int x, int y)
30 {
31     x -= y;
32     x += (x < 0) ? m : 0;
33     return x;
34 }
35 
36 int main()
37 {
38     scanf("%d%d", &n, &m); int tmp = m; f[0][0] = 1;
39     while (tmp % 2 == 0) t++, tmp >>= 1;   t++;
40     for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
41     for (int i = 1; i <= n; i++)
42     {
43         for (int j = 0; j < m; j++) g[j] = f[t][j];
44         for (int j = 0; j < m; j++) add_mod(f[t][j], g[mi(j, a[i])]);
45         for (int k = t; k; k--)
46             for (int j = 0; j < m; j++)
47                 add_mod(f[k][j], f[k - 1][mi(j, a[i])]);
48     }
49     for (int i = 0; i < m; i++)
50         for (int j = 1; j <= t; j++)
51         {
52             int x = i;
53             for (int k = 1; k < j; k++) x = (x << 1) % m;
54             if (!x) add_mod(ans, f[j][i]);
55         }
56     printf("%d\n", ans);
57     return 0;
58 }
View Code

D:

一股线段树的味道。如果是单点修改,这题就是⼀个线段树维护区间线性基的题,但这⾥是区间xor。

考虑原序列的xor差分,这样每次修改时只要修改两个位置的值即可。查询时只要特判边界,因为维护xor差分的线性基和原序列的线性基等价。总复杂度O(nlogn(logw)^2)。

  1 /* basic header */
  2 #include <bits/stdc++.h>
  3 /* define */
  4 #define ll long long
  5 #define dou double
  6 #define pb emplace_back
  7 #define mp make_pair
  8 #define sot(a,b) sort(a+1,a+1+b)
  9 #define rep1(i,a,b) for(int i=a;i<=b;++i)
 10 #define rep0(i,a,b) for(int i=a;i<b;++i)
 11 #define eps 1e-8
 12 #define int_inf 0x3f3f3f3f
 13 #define ll_inf 0x7f7f7f7f7f7f7f7f
 14 #define lson curpos<<1
 15 #define rson curpos<<1|1
 16 /* namespace */
 17 using namespace std;
 18 /* header end */
 19 
 20 const int maxn = 5e4 + 10;
 21 int n, m, l[maxn << 2], r[maxn << 2], bit[maxn], a[maxn], mx;
 22 void addbit(int x, int y)
 23 {
 24     while (x <= n)
 25     {
 26         bit[x] ^= y;
 27         x += x & -x;
 28     }
 29 }
 30 
 31 int sumbit(int x)
 32 {
 33     int ret = 0;
 34     while (x)
 35     {
 36         ret ^= bit[x];
 37         x -= x & -x;
 38     }
 39     return ret;
 40 }
 41 
 42 struct Base
 43 {
 44     int b[32];
 45     void ins(int v)
 46     {
 47         for (int i = 30; ~i; i--)
 48             if (v & (1 << i))
 49             {
 50                 if (!b[i])
 51                 {
 52                     b[i] = v;
 53                     return;
 54                 }
 55                 else v ^= b[i];
 56             }
 57     }
 58     int work(int v)
 59     {
 60         for (int i = 30; ~i; i--) v = max(v, v ^ b[i]);
 61         return v;
 62     }
 63     void clear()
 64     {
 65         memset(b, 0, sizeof(b));
 66     }
 67 } tree[maxn << 2], e;
 68 
 69 Base merge(Base x, Base y)
 70 {
 71     for (int i = 30; ~i; i--)
 72         if (x.b[i]) y.ins(x.b[i]);
 73     return y;
 74 }
 75 
 76 void build(int curpos, int curl, int curr)
 77 {
 78     l[curpos] = curl, r[curpos] = curr;
 79     if (curl == curr)
 80     {
 81         tree[curpos].ins(a[curl]); return;
 82     }
 83     int mid = curl + curr >> 1;
 84     build(lson, curl, mid); build(rson, mid + 1, curr);
 85     tree[curpos] = merge(tree[lson], tree[rson]);
 86 }
 87 
 88 void modify(int curpos, int x, int p)
 89 {
 90     if (l[curpos] == r[curpos])
 91     {
 92         tree[curpos].clear();
 93         a[x] ^= p;
 94         addbit(x, p);
 95         tree[curpos].ins(a[x]);
 96         return;
 97     }
 98     int mid = l[curpos] + r[curpos] >> 1;
 99     if (x <= mid) modify(lson, x, p);
100     else modify(rson, x, p);
101     tree[curpos] = merge(tree[lson], tree[rson]);
102 }
103 
104 Base query(int curpos, int curl, int curr)
105 {
106     if (curl > curr) return e;
107     if (l[curpos] == curl && r[curpos] == curr) return tree[curpos];
108     int mid = l[curpos] + r[curpos] >> 1;
109     if (curr <= mid) return query(lson, curl, curr);
110     else if (curl > mid) return query(rson, curl, curr);
111     else return merge(query(lson, curl, mid), query(rson, mid + 1, curr));
112 }
113 
114 int main()
115 {
116     scanf("%d%d", &n, &m);
117     for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
118     for (int i = n; i; i--) a[i] ^= a[i - 1], addbit(i, a[i]);
119     build(1, 0, n);
120     while (m--)
121     {
122         int op, l, r, v; scanf("%d%d%d%d", &op, &l, &r, &v);
123         if (op == 1)
124         {
125             modify(1, l, v);
126             if (r < n) modify(1, r + 1, v);
127         }
128         else
129         {
130             ll ans = max(query(1, l + 1, r).work(v), query(1, l + 1, r).work(v ^ sumbit(l)));
131             printf("%lld\n", ans);
132         }
133     }
134     return 0;
135 }
View Code

E:

dreamoon出的神仙数学题,再见。

F:

题目出得非常莫队。每次把区间[l,r]扩展到[l,r+1]时需要维护a[r+1]和[l,r]这段区间的贡献。这里要利用到莫队二次离线的trick:

莫队可以抽象为O(n*sqrt(m))次转移,每次查询一个序列中的位置对一个区间[l,r]的贡献。

这个贡献是可以差分的,也就是说把每次转移的a[r+1]对区间[l,r]的贡献差分为a[r+1]对前缀[1,r]的贡献减去对前缀[1,l-1]的贡献。

然后通过这个差分我们就可以发现,我们把O(n*sqrt(m))次的修改降低到了O(n)次修改,因为前缀只会扩展O(n)次。于是我们每次可以较高复杂度扩展前缀和,因为插入次数变成了O(n)次。

然后我们离线莫队转移的时候可以做到线性空间,因为每次莫队是从一个区间转移到另一个区间。我们记pre[i]为i的前缀区间的答案,suf[i]为i的后缀区间的答案。考虑把[l1,r1]扩展到区间[l1,r2],我们可以把区间[r1,r2]的贡献差分为pre[r2]-pre[r1]-(区间[1,l1-1]对区间[r1+1,r2]的贡献)。再次差分区间[1,l1-1]对区间[r1+1,r2]的贡献为[1,l1-1]对[1,r2]的贡献减去[1,l1-1]对区间[1,r1]的贡献,每个位置开个vector保存即可。

于是我们达成了O(n+m)的空间复杂度把莫队二次离线,现在我们要考虑怎么算这个贡献。先把贡献拆开变成两部分:[l,r]中x的倍数个数,[l,r]中x的约数个数。

[l,r]中x的倍数个数:每次前缀加⼊⼀个数a[i]的时候,我们把a[i]的约数位置所对应的数组pre1的位置都++然后查询的时候直接查pre1[x]即可得到x的倍数个数。

[l,r]中x的约数个数:这⾥考虑对x的约数进⾏⼀次根号分治。对于所有x⼤于sqrt(n)的约数,我们拓展前缀的时候维护,即每次把a[i]的倍数位置所对应的数组pre2的位置都++然后查询的时候直接查pre2[x]即可得到x大于sqrt的约数个数。对于所有x小于sqrt(n)的约数,我们考虑每次直接枚举区间[l,r]中1,2,...,sqrt(n)的出现次数。这里可以用fractionalcascading的trick做到O(sqrt(n)+logn)单次,线性空间。

整个做法时间复杂度O(n*sqrt(m)),空间复杂度O(n+m)。

猜你喜欢

转载自www.cnblogs.com/JHSeng/p/10883373.html