A
小水题。
直接枚举后排序即可。
#include <bits/stdc++.h>
using namespace std;
inline int gi()
{
char c = getchar();
while(c < '0' || c > '9') c = getchar();
int sum = 0;
while('0' <= c && c <= '9') sum = sum * 10 + c - 48, c = getchar();
return sum;
}
const int maxn = 505;
int n, k, a[maxn];
int tmp[maxn * maxn], cnt;
int main()
{
n = gi(); k = gi();
for (int i = 1; i <= n; ++i) a[i] = gi();
for (int i = 1; i <= n; ++i)
for (int j = i + 1; j <= n; ++j)
tmp[++cnt] = a[i] + a[j];
sort(tmp + 1, tmp + cnt + 1, greater<int>());
long long ans = 0;
for (int i = 1; i <= k; ++i) ans += tmp[i];
printf("%lld\n", ans);
return 0;
}
B
小水题。
直接设 f i , 1 / 2 / 3 f_{i,1/2/3} fi,1/2/3分别表示填到了第 i i i列,只填了第一行/只填了第二行/两行都填了的最小代价。转移显然。
#include <bits/stdc++.h>
using namespace std;
inline int gi()
{
char c = getchar();
while(c < '0' || c > '9') c = getchar();
int sum = 0;
while('0' <= c && c <= '9') sum = sum * 10 + c - 48, c = getchar();
return sum;
}
const int maxn = 200005;
int n, p[5][maxn], vec[maxn], cnt, ans;
int f[10][maxn];
int main()
{
n = gi();
for (int i = 1; i <= n; ++i) p[0][i] = gi();
for (int i = 1; i <= n; ++i) p[1][i] = gi();
for (int i = 1; i <= n; ++i)
if (p[0][i] || p[1][i]) vec[++cnt] = i;
if (!p[1][vec[1]]) {
f[1][1] = 0;
f[3][1] = 1;
f[2][1] = 1e9;
} else if (!p[2][vec[1]]) {
f[2][1] = 0;
f[3][1] = 1;
f[1][1] = 1e9;
} else f[3][1] = 0, f[2][1] = f[1][1] = 1e9;
for (int i = 2; i <= cnt; ++i) {
int j = vec[i];
if (!p[1][j]) {
f[1][i] = min(f[1][i - 1], f[3][i - 1]) + j - vec[i - 1] - 1;
f[3][i] = min(f[1][i] + 1, min(f[2][i - 1], f[3][i - 1]) + j - vec[i - 1]);
f[2][i] = 1e9;
} else if (!p[0][j]) {
f[2][i] = min(f[2][i - 1], f[3][i - 1]) + j - vec[i - 1] - 1;
f[3][i] = min(f[2][i] + 1, min(f[1][i - 1], f[3][i - 1]) + j - vec[i - 1]);
f[1][i] = 1e9;
} else {
f[3][i] = min(min(f[1][i - 1], f[2][i - 1]), f[3][i - 1]) + j - vec[i - 1] - 1;
f[1][i] = f[2][i] = 1e9;
}
}
printf("%d\n", min(f[3][cnt], min(f[1][cnt], f[2][cnt])));
return 0;
}
C
一个序列的每个非空子序列中的整数之和为 2 l e n − 1 ∑ a i 2^{len-1} \sum{a_i} 2len−1∑ai。现在要求这个式子 m o d m mod m modm等于 0 0 0。不妨设 m m m含有 k k k个 2 2 2因子,除去这些因子得到的数为 M M M(比如当 m = 12 m=12 m=12时, k = 2 k=2 k=2, M = 3 M=3 M=3)。可以发现当 l e n > k len > k len>k时,我们要求 ∑ a i m o d    M = 0 \sum{a_i} \mod M=0 ∑aimodM=0即可。所以对于 l e n ≤ k len \leq k len≤k的序列,我们记录长度和非空子序列之和来暴力DP。剩下的序列只需要记录序列和即可,注意要减去长度 ≤ k \leq k ≤k的序列。
#include <bits/stdc++.h>
using namespace std;
inline int gi()
{
char c = getchar();
while(c < '0' || c > '9') c = getchar();
int sum = 0;
while('0' <= c && c <= '9') sum = sum * 10 + c - 48, c = getchar();
return sum;
}
const int maxn = 5005, mod = 1e9 + 7;
int n, m, a[maxn], f[20][maxn], g[20][maxn], tmp[maxn], h[maxn];
inline int fpow(int x, int k)
{
int res = 1;
while (k) {
if (k & 1) res = res * x % m;
k >>= 1; x = x * x % m;
}
return res;
}
int main()
{
n = gi(); m = gi();
for (int i = 1; i <= n; ++i) a[i] = gi();
int k = 0, tm = m;
while (tm % 2 == 0) tm >>= 1, ++k;
f[0][0] = 1;
for(int i = 1; i <= n; ++i)
for (int j = min(k, i); j; --j) {
for (int k = 0; k < m; ++k) {
f[j][k] += f[j - 1][k >= a[i] ? k - a[i] : k - a[i] + m];
if (f[j][k] >= mod) f[j][k] -= mod;
}
}
for (int i = 1; i <= n; ++i) a[i] %= tm;
g[0][0] = 1; h[0] = 1;
for(int i = 1; i <= n; ++i) {
for (int j = min(k, i); j; --j)
for (int k = 0; k < tm; ++k) {
g[j][k] += g[j - 1][k >= a[i] ? k - a[i] : k - a[i] + tm];
if (g[j][k] >= mod) g[j][k] -= mod;
}
for (int k = 0; k < tm; ++k) tmp[k] = h[k];
for (int k = 0; k < tm; ++k) {
h[k] += tmp[k >= a[i] ? k - a[i] : k - a[i] + tm];
if (h[k] >= mod) h[k] -= mod;
}
}
int ans = h[0];
for (int i = 0; i <= k; ++i) {
ans -= g[i][0];
if (ans < 0) ans += mod;
}
for (int i = 1; i <= k; ++i)
for (int j = 0; j < m; ++j)
if (j * fpow(2, i - 1) % m == 0) {
ans += f[i][j];
if (ans >= mod) ans -= mod;
}
printf("%d\n", ans);
return 0;
}
D
如果这题只要求单点修改,那么就是一道小水题。考虑设 b i = a i − 1 ⊕ a i b_i=a_{i-1} \oplus a_i bi=ai−1⊕ai,那么修改就变成了单点修改。根据简单的线性代数知识,查询 a i ( i ∈ [ l , r ] ) a_i(i \in [l,r]) ai(i∈[l,r])组成的线性基等价于查询 a l ∪ b i ( i ∈ [ l + 1 , r ] ) a_l \cup b_i(i \in [l+1,r]) al∪bi(i∈[l+1,r])组成的线性基。
p.s. 这道题有双倍经验 C F 587 E CF587E CF587E。
#include <bits/stdc++.h>
using namespace std;
inline int gi()
{
char c = getchar();
while(c < '0' || c > '9') c = getchar();
int sum = 0;
while('0' <= c && c <= '9') sum = sum * 10 + c - 48, c = getchar();
return sum;
}
const int maxn = 200005;
int n, q, a[maxn], b[maxn], sum[maxn];
int p[maxn * 4][30], tmp[30];
#define lch (s << 1)
#define rch (s << 1 | 1)
#define mid ((l + r) >> 1)
void merge(int *p, int *p1, int *p2)
{
memcpy(p, p1, sizeof(int) * 30);
for (int i = 29, v; ~i; --i) {
if (!p2[i]) continue;
v = p2[i];
for (int j = 29; ~j; --j)
if ((v >> j) & 1) {
if (!p[j]) {p[j] = v; break;}
else v ^= p[j];
}
}
}
void build(int s, int l, int r)
{
if (l == r) {
for (int i = 29; ~i; --i)
if ((a[l] >> i) & 1) return p[s][i] = a[l], void();
return ;
}
build(lch, l, mid);
build(rch, mid + 1, r);
merge(p[s], p[lch], p[rch]);
}
void modify(int s, int l, int r, int x, int v)
{
if (l == r) {
a[l] ^= v;
memset(p[s], 0, sizeof(p[s]));
for (int i = 29; ~i; --i)
if ((a[l] >> i) & 1) return p[s][i] = a[l], void();
return ;
}
if (x <= mid) modify(lch, l, mid, x, v);
else modify(rch, mid + 1, r, x, v);
merge(p[s], p[lch], p[rch]);
}
void query(int s, int l, int r, int ql, int qr, int *tmp)
{
if (ql <= l && r <= qr) return merge(tmp, tmp, p[s]);
if (ql <= mid) query(lch, l, mid, ql, qr, tmp);
if (qr > mid) query(rch, mid + 1, r, ql, qr, tmp);
}
void insert(int x, int v)
{
while (x <= n) {
sum[x] ^= v;
x += x & (-x);
}
}
int query(int x)
{
int res = 0;
while (x) {
res ^= sum[x];
x -= x & (-x);
}
return res;
}
int main()
{
n = gi(); q = gi();
for (int i = 1; i <= n; ++i)
b[i] = gi(), insert(i, b[i]), insert(i + 1, b[i]);
for (int i = n; i >= 1; --i) a[i] = b[i] ^ b[i - 1];
build(1, 1, n);
int op, l, r, v;
while (q--) {
op = gi(); l = gi(); r = gi();
if (op == 1) {
v = gi();
modify(1, 1, n, l, v);
if (r < n) modify(1, 1, n, r + 1, v);
insert(l, v); insert(r + 1, v);
} else {
memset(tmp, 0, sizeof(tmp));
if (l < r) query(1, 1, n, l + 1, r, tmp);
int ans = gi(), v = query(l);
for (int j = 29; ~j; --j)
if ((v >> j) & 1) {
if (!tmp[j]) {tmp[j] = v; break;}
else v ^= tmp[j];
}
for (int i = 29; ~i; --i)
if ((ans ^ tmp[i]) > ans) ans ^= tmp[i];
printf("%d\n", ans);
}
}
return 0;
}
E
先把整除变成一般乘法。
(
x
−
x
m
o
d
  
i
)
÷
i
×
j
=
(
x
×
j
−
x
×
j
m
o
d
  
i
)
÷
i
(x-x \mod i) \div i \times j = (x \times j - x \times j \mod i) \div i
(x−xmodi)÷i×j=(x×j−x×jmodi)÷i
(
x
−
x
m
o
d
  
i
)
×
j
=
x
×
j
−
x
×
j
m
o
d
  
i
(x-x \mod i) \times j = x \times j - x \times j \mod i
(x−xmodi)×j=x×j−x×jmodi
x
m
o
d
  
i
×
j
=
x
×
j
m
o
d
  
i
x \mod i \times j = x \times j \mod i
xmodi×j=x×jmodi
x
m
o
d
  
i
×
j
=
x
m
o
d
  
i
×
j
m
o
d
  
i
x \mod i \times j = x \mod i \times j \mod i
xmodi×j=xmodi×jmodi
设
v
=
x
m
o
d
  
i
×
j
v=x \mod i \times j
v=xmodi×j,则上式可化为
v
=
v
m
o
d
  
i
v=v \mod i
v=vmodi,即
v
<
i
v < i
v<i。
所以 x m o d    i × j < i x \mod i \times j < i xmodi×j<i,所以 j < ⌈ i x m o d    i ⌉ j < \lceil \frac {i} {x \mod i} \rceil j<⌈xmodii⌉。
所以答案为 ∑ i = 1 a m i n ( b , ⌈ i x m o d    i ⌉ − 1 ) \sum_{i=1}^a min(b,\lceil \frac {i} {x \mod i} \rceil - 1) ∑i=1amin(b,⌈xmodii⌉−1)。
分两种情况考虑。
第一种情况, i > x i > x i>x,那么 x m o d    i = x x \mod i = x xmodi=x。
所求为 ∑ i = x + 1 a m i n ( b , ⌈ i x m o d    i ⌉ − 1 ) \sum_{i=x+1}^a min(b, \lceil \frac {i} {x \mod i} \rceil - 1) ∑i=x+1amin(b,⌈xmodii⌉−1),直接推下式子 O ( 1 ) O(1) O(1)算即可。
第二种情况, i ≤ x i \leq x i≤x。
考虑到 x m o d    i = x − ⌊ x i ⌋ × i x \mod i = x - \lfloor \frac {x} {i} \rfloor \times i xmodi=x−⌊ix⌋×i
所以先固定 ⌊ x i ⌋ \lfloor \frac {x} {i} \rfloor ⌊ix⌋,然后固定 x x − ⌊ i i ⌋ × i \frac {x} {x - \lfloor \frac {i} {i} \rfloor \times i} x−⌊ii⌋×ix。那么满足二元组 ( ⌊ x i ⌋ , x x − ⌊ i i ⌋ × i ) (\lfloor \frac {x} {i} \rfloor,\frac {x} {x - \lfloor \frac {i} {i} \rfloor \times i}) (⌊ix⌋,x−⌊ii⌋×ix)相等的一定是一段区间。可以证明,区间数是 O ( n l o g n ) O(\sqrt n logn) O(nlogn)的,那么暴力数论分块算即可。
设当前二元组是 ( c , q ) (c,q) (c,q),下面介绍求当前二元组的右端点的方法。
⌊ i x − c i ⌋ ≤ q \lfloor \frac {i} {x-ci} \rfloor \leq q ⌊x−cii⌋≤q
i ≤ q ( x − c i ) i \leq q(x-ci) i≤q(x−ci)
i ≤ q x q + 1 i \leq \frac {qx} {q+1} i≤q+1qx
i ≤ ⌊ q x q + 1 ⌋ i \leq \lfloor \frac {qx} {q+1} \rfloor i≤⌊q+1qx⌋
所以右端点为 ⌊ q x q + 1 ⌋ \lfloor \frac {qx} {q+1} \rfloor ⌊q+1qx⌋。
#include <bits/stdc++.h>
using namespace std;
inline int gi()
{
char c = getchar();
while(c < '0' || c > '9') c = getchar();
int sum = 0;
while('0' <= c && c <= '9') sum = sum * 10 + c - 48, c = getchar();
return sum;
}
typedef long long ll;
#define int ll
int x, a, b;
ll res, ans;
ll calc(int l, int r, int c)
{
ll res = 0;
if (x % r == 0) res += b, --r;
for (int i = l, j, q; i <= r; i = j + 1) {
q = (i + x - (ll)c * i - 1) / (x - (ll)c * i);
j = min((ll)r, (ll)q * x / ((ll)c * q + 1));
res += (ll)(j - i + 1) * min(b, q - 1);
}
return res;
}
signed main()
{
int T = gi();
while (T--) {
x = gi(); a = gi(); b = gi(); ans = 0;
if (a > x) {
int y = min(a, b * x + 1);
ans += (a - y) * b; --y;
int tmp = y / x - 1;
ans += (ll)tmp * (tmp + 1) / 2 * x + (y % x + 1) * (y / x);
a = x;
}
for (int i = 1, j; i <= a; i = j + 1) {
j = min(a, x / (x / i));
ans += calc(i, j, x / i);
}
printf("%lld\n", ans);
}
return 0;
}
F
分块什么的,咕掉吧。