好久之前打的,今晚才有空补题。不得不说这个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 }
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 }
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 }
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 }
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)。