2019 ICPC Asia Yinchuan Regional
A - Girls Band Party(分组背包)
每个物品有两个标签,名字,颜色,当名字是设置为奖赏时会对整体加上0.1 的贡献,如果颜色符合要求时 会对整体加上 0.2 的的贡献
但是有一个限制,相同名字的只能选一次,我们的目的是要选出 5 个物品使得总价值最大,map 映射一下,然后做分组背包就好了,
定义 f [ i ] [ j ] f[i][j] f[i][j]为装了 i i i个物品,附加值是 j 10 \frac{j}{10} 10j时,最大的价值,最后再遍历一遍数组统计一下答案即可,整体复杂度 5 × 15 × n × T 5 \times 15 \times n \times T 5×15×n×T。
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
string name[N], col[N];
int power[N], f[10][20], n, tot;
vector<pair<int, int>> a[N];
map<string, int> mp, mp1;
int main() {
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
int T;
cin >> T;
while (T--) {
cin >> n;
memset(f, -1, sizeof f), tot = 0;
mp1.clear(), mp.clear();
for (int i = 1; i <= n; i++) {
cin >> name[i] >> col[i] >> power[i];
if (!mp.count(name[i])) {
mp[name[i]] = ++tot;
a[tot].clear();
}
}
for (int i = 1; i <= 5; i++) {
string str;
cin >> str;
mp1[str] = 1;
}
string b_col;
cin >> b_col;
for (int i = 1; i <= n; i++) {
int cur = 0;
if (mp1.count(name[i])) {
cur += 1;
}
if (col[i] == b_col) {
cur += 2;
}
a[mp[name[i]]].push_back({power[i], cur});
}
f[0][0] = 0;
for (int i = 1; i <= tot; i++) {
for (int j = 5; j >= 1; j--) {
for (int k = 15; k >= 0; k--) {
for (auto it : a[i]) {
if (k >= it.second && f[j - 1][k - it.second] != -1) {
f[j][k] = max(f[j][k], f[j - 1][k - it.second] + it.first);
}
}
}
}
}
int ans = 0;
for (int i = 1; i <= 5; i++) {
for (int j = 0; j <= 15; j++) {
ans = max(ans, f[i][j] * (10 + j) / 10);
}
}
cout << ans << "\n";
}
return 0;
}
B - So Easy(模拟,签到)
#include <bits/stdc++.h>
using namespace std;
const int N = 1e3 + 10;
int a[N][N], r[N], n, x, y;;
int main() {
// freopen("in.txt", "r", stdin);
// freopen("out.txt", "w", stdout);
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
int minn = 0x3f3f3f3f;
for (int j = 1; j <= n; j++) {
scanf("%d", &a[i][j]);
if (a[i][j] == -1) {
a[i][j] = 0x3f3f3f3f;
x = i, y = j;
}
minn = min(minn, a[i][j]);
}
r[i] = minn;
for (int j = 1; j <= n; j++) {
a[i][j] -= minn;
}
}
int minn = 0x3f3f3f3f;
for (int i = 1; i <= n; i++) {
minn = min(minn, a[i][y]);
}
printf("%d\n", r[x] + minn);
return 0;
}
D - Easy Problem(莫比乌斯)
定义一个序列是 ( n , m , d ) (n, m, d) (n,m,d) - good,当且仅当 1 ≤ a i ≤ m 1 \leq a_i \leq m 1≤ai≤m, gcd ( a 1 , a 2 , … , a n ) = d \gcd(a_1, a_2, \dots, a_n) = d gcd(a1,a2,…,an)=d,
f
(
q
,
k
)
f(q, k)
f(q,k)是对于给定的
(
n
,
m
,
d
)
(n, m, d)
(n,m,d)序列
q
q
q,
f
(
(
a
1
,
a
2
,
…
,
a
n
)
,
k
)
=
(
a
1
a
2
…
a
n
)
k
f((a_1, a_2, \dots, a_n), k) = (a_1a_2 \dots a_n) ^ k
f((a1,a2,…,an),k)=(a1a2…an)k,给定
n
,
m
,
d
,
k
n, m, d, k
n,m,d,k要求所有的
f
(
q
,
k
)
f(q, k)
f(q,k)的和。
∑
a
1
=
1
m
∑
a
2
=
1
m
⋯
∑
a
n
=
1
m
(
a
1
a
2
…
a
n
)
K
[
gcd
(
a
1
,
a
2
,
…
,
a
n
)
=
d
]
d
K
n
∑
a
1
=
1
m
d
∑
a
2
=
1
m
d
⋯
∑
a
n
=
1
m
d
(
a
1
a
2
…
a
n
)
K
[
gcd
(
a
1
,
a
2
,
…
,
a
n
)
=
1
]
d
k
n
∑
k
=
1
m
d
k
K
n
μ
(
k
)
(
∑
i
=
1
m
k
d
i
k
)
n
\sum_{a_1 = 1} ^{m} \sum_{a_2 = 1} ^{m} \dots \sum_{a_n = 1} ^{m} (a_1 a_2 \dots a_n) ^ K[\gcd(a_1, a_2, \dots, a_n) = d]\\ d ^ {K n} \sum_{a_1 = 1} ^{\frac{m}{d}} \sum_{a_2 = 1} ^{\frac{m}{d}} \dots \sum_{a_n = 1} ^{\frac{m}{d}}(a_1a_2\dots a_n) ^ K[\gcd(a_1, a_2, \dots, a_n) = 1]\\ d ^{kn} \sum_{k = 1} ^{\frac{m}{d}} k ^{Kn} \mu(k) (\sum_{i = 1} ^{\frac{m}{kd}} i ^ k) ^ n\\
a1=1∑ma2=1∑m⋯an=1∑m(a1a2…an)K[gcd(a1,a2,…,an)=d]dKna1=1∑dma2=1∑dm⋯an=1∑dm(a1a2…an)K[gcd(a1,a2,…,an)=1]dknk=1∑dmkKnμ(k)(i=1∑kdmik)n
之后只要欧拉降幂搞一搞就行了,整体复杂度 O ( m log m ) O(\sqrt m \log m) O(mlogm),但是好像没必要,直接 O ( m log m ) O(m \log m) O(mlogm)做就行了。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5 + 10, mod = 59964251, phi = 59870352;
int mu[N], prime[N], cnt;
ll m, d, k, n, sum[N];
bool st[N];
char str[N];
ll quick_pow(ll a, int n, int mod = 59964251) {
ll ans = 1;
while(n) {
if(n & 1) ans = ans * a % mod;
a = a * a % mod;
n >>= 1;
}
return ans;
}
void init() {
memset(st, 0, sizeof st);
cnt = 0;
mu[1] = 1, sum[1] = 1;
for(int i = 2; i < N; i++) {
if(!st[i]) {
mu[i] = -1;
prime[cnt++] = i;
sum[i] = quick_pow(i, k);
}
for(int j = 0; j < cnt && i * prime[j] < N; j++) {
st[i * prime[j]] = 1;
sum[i * prime[j]] = sum[i] * sum[prime[j]] % mod;
if(i % prime[j] == 0) break;
mu[i * prime[j]] = -mu[i];
}
}
for(int i = 1; i < N; i++) {
sum[i] = (sum[i] + sum[i - 1]) % mod;
}
}
ll solve(ll m) {
ll ans = 0;
for (ll i = 1; i <= m; i++) {
ans = ans + 1ll * mu[i] * quick_pow(i, k * n % phi + phi) % mod * quick_pow(sum[m / i], n + phi) % mod;
}
return (ans % mod + mod) % mod;
}
int main() {
// freopen("in.txt", "r", stdin);
// freopen("out.txt", "w", stdout);
int T;
scanf("%d", &T);
while (T--) {
scanf("%s %lld %lld %lld\n", str + 1, &m, &d, &k);
init();
int len = strlen(str + 1);
n = 0;
for (int i = 1; i <= len; i++) {
n = n * 10 + str[i] - '0';
n %= phi;
}
ll ans = quick_pow(d, k * n % phi + phi) * solve(m / d) % mod;
printf("%lld\n", ans);
}
return 0;
}
E - XOR Tree(长链剖分)
给定一颗根节点为 1 1 1的有 n n n个节点的树点有点权 a i a_i ai, d ( x , y ) d(x, y) d(x,y)表示 x , y x, y x,y之间的边数,集合 P ( x , k ) = { a y ∣ y i s t h e s u b t r e e o f x a n d d ( x , y ) ≤ k } P(x, k) = \{a_y \mid y\ is\ the\ subtree\ of\ x\ and\ d(x, y) \leq k \} P(x,k)={ay∣y is the subtree of x and d(x,y)≤k}, a x ∈ P ( x , k ) a_x \in P(x, k) ax∈P(x,k)
定义一个集合的价值为,加入给定一个集合 { 1 , 1 , 2 , 3 } \{1, 1, 2, 3\} {1,1,2,3},则其价值为 ( 1 ⊕ 1 ) 2 + ( 1 ⊕ 2 ) 2 + ( 1 ⊕ 3 ) 2 + ( 1 ⊕ 2 ) 2 + ( 1 ⊕ 3 ) 2 + ( 2 ⊕ 3 ) 2 = 27 (1 \oplus 1) ^ 2 + (1 \oplus 2) ^ 2 + (1 \oplus 3) ^ 2 + (1 \oplus 2) ^ 2 + (1 \oplus 3) ^ 2 + (2 \oplus 3) ^ 2 = 27 (1⊕1)2+(1⊕2)2+(1⊕3)2+(1⊕2)2+(1⊕3)2+(2⊕3)2=27。
给定 k k k,要我们求出 P ( i , k ) , i ∈ [ 1 , n ] P(i, k), i \in [1, n] P(i,k),i∈[1,n],答案对 2 64 2 ^ {64} 264取膜。
题目大概是要我们算这样一个东西:
∑
i
=
1
n
∑
j
=
i
+
1
n
(
a
i
⊕
a
j
)
2
\sum_{i = 1} ^{n} \sum_{j = i + 1} ^{n} (a_i \oplus a_j) ^ 2\\
i=1∑nj=i+1∑n(ai⊕aj)2
对二进制拆位后算贡献:
∑
i
=
1
n
∑
j
=
i
+
1
n
(
(
a
i
,
0
⊕
a
j
,
0
)
2
0
+
(
a
i
,
1
⊕
a
j
,
1
)
2
1
+
⋯
+
(
a
i
,
29
⊕
a
j
,
29
)
2
29
)
2
∑
i
=
1
n
∑
j
=
i
+
1
n
∑
k
1
=
0
29
∑
k
2
=
0
29
(
a
i
,
k
1
⊕
a
j
,
k
1
)
(
a
i
,
k
2
⊕
a
j
,
k
2
)
2
k
1
+
k
2
∑
k
1
=
0
29
∑
k
2
=
0
29
2
k
1
+
k
2
∑
i
=
1
n
∑
j
=
k
+
1
n
[
a
i
,
k
1
≠
a
j
,
k
1
]
[
a
i
,
k
2
≠
a
j
,
k
2
]
\sum_{i = 1} ^{n} \sum_{j = i + 1} ^{n} \left((a_{i, 0} \oplus a_{j, 0}) 2 ^ 0 + (a_{i, 1} \oplus a_{j, 1}) 2 ^1 + \dots + (a_{i, 29} \oplus a_{j, 29}) 2 ^{29} \right) ^ 2\\ \sum_{i = 1} ^{n} \sum_{j = i + 1} ^{n} \sum_{k1 = 0} ^{29} \sum_{k2 = 0} ^{29} (a_{i, k_1} \oplus a_{j, k1})(a_{i, k2} \oplus a_{j, k2}) 2 ^{k1 + k2}\\ \sum_{k1 = 0} ^{29} \sum_{k2 = 0} ^{29} 2 ^{k1 + k2} \sum_{i = 1} ^{n} \sum_{j = k + 1} ^{n} [a_{i, k1} \neq a_{j, k1}][a_{i, k2} \neq a_{j, k2}]\\
i=1∑nj=i+1∑n((ai,0⊕aj,0)20+(ai,1⊕aj,1)21+⋯+(ai,29⊕aj,29)229)2i=1∑nj=i+1∑nk1=0∑29k2=0∑29(ai,k1⊕aj,k1)(ai,k2⊕aj,k2)2k1+k2k1=0∑29k2=0∑292k1+k2i=1∑nj=k+1∑n[ai,k1=aj,k1][ai,k2=aj,k2]
F - Function!(分类讨论)
f
a
(
x
)
=
a
x
(
a
>
0
,
a
≠
1
)
f_a(x) = a ^ x(a > 0, \ a \neq 1)
fa(x)=ax(a>0, a=1),我们要求
∑
a
=
2
n
(
a
∑
b
=
a
n
⌊
f
a
−
1
(
b
)
⌋
⌈
f
b
−
1
(
a
)
⌉
)
\sum\limits_{a = 2} ^{n} \left(a \sum\limits_{b = a} ^{n} \lfloor f_a ^{-1}(b) \rfloor \lceil f_b ^{-1}(a) \rceil \right)
a=2∑n(ab=a∑n⌊fa−1(b)⌋⌈fb−1(a)⌉)。
∑
a
=
2
n
(
a
∑
b
=
a
n
⌊
f
a
−
1
(
b
)
⌋
⌈
f
b
−
1
(
a
)
⌉
)
∑
a
=
2
n
(
a
∑
b
=
a
n
⌊
log
a
b
⌋
⌈
log
b
a
⌉
)
b
≥
a
,
则
有
⌈
log
b
a
⌉
=
1
∑
a
=
2
n
(
a
∑
b
=
a
n
⌊
log
a
b
⌋
)
\sum\limits_{a = 2} ^{n} \left(a \sum\limits_{b = a} ^{n} \lfloor f_a ^{-1}(b) \rfloor \lceil f_b ^{-1}(a) \rceil \right)\\ \sum_{a = 2} ^{n} \left( a \sum_{b = a} ^{n} \lfloor \log_a b \rfloor \lceil \log_b a \rceil \right)\\ b \geq a,则有 \lceil \log _b a \rceil = 1\\ \sum_{a = 2} ^{n} \left( a \sum_{b = a} ^{n} \lfloor \log_a b \rfloor\right)\\
a=2∑n(ab=a∑n⌊fa−1(b)⌋⌈fb−1(a)⌉)a=2∑n(ab=a∑n⌊logab⌋⌈logba⌉)b≥a,则有⌈logba⌉=1a=2∑n(ab=a∑n⌊logab⌋)
且容易发现,当 a × a > n a \times a > n a×a>n时,有 ⌊ log a b ⌋ \lfloor \log _a b \rfloor ⌊logab⌋恒为 1 1 1,所以可以单独用公式计算。
#include <bits/stdc++.h>
using namespace std;
const int mod = 998244353, inv2 = mod + 1 >> 1, inv6 = (mod + 1) / 6;
typedef long long ll;
ll calc1(ll l, ll r) {
return (l + r) % mod * ((r - l + 1) % mod) % mod * inv2 % mod;
}
ll calc2(ll n) {
n %= mod;
return n * (n + 1) % mod * (2 * n + 1) % mod * inv6 % mod;
}
int main() {
// freopen("in.txt", "r", stdin);
// freopen("out.txt", "w", stdout);
ll a, n, ans = 0;
cin >> n;
for (a = 2; a * a <= n; a++) {
for (ll l = a, r, k = 1; l <= n; l = r + 1, k++) {
r = min(l * a - 1, n);
int tot = (r - l + 1) % mod;
ans = (ans + a * tot % mod * k % mod) % mod;
}
}
// for (; a <= n; a++) {//原本我们是这样算的,当时这里可以变成,自然幂次求和的形式,所以可以快速算出来。
// ans = (ans + a * (n - a + 1) % mod) % mod;
// }
ans = (ans + (n + 1) % mod * calc1(a, n) % mod) % mod;
ans = ((ans - calc2(n) + calc2(a - 1)) % mod + mod) % mod;
cout << ans << "\n";
return 0;
}
G - Pot!!(区间最大值)
#include <bits/stdc++.h>
#define mid (l + r >> 1)
#define lson rt << 1, l, mid
#define rson rt << 1 | 1, mid + 1, r
#define ls rt << 1
#define rs rt << 1 | 1
using namespace std;
const int N = 1e5 + 10;
struct Res {
int a[N << 2], lazy[N << 2];
void push_up(int rt) {
a[rt] = max(a[ls], a[rs]);
}
void push_down(int rt) {
if (lazy[rt]) {
a[ls] += lazy[rt], a[rs] += lazy[rt];
lazy[ls] += lazy[rt], lazy[rs] += lazy[rt];
lazy[rt] = 0;
}
}
void update(int rt, int l, int r, int L, int R, int v) {
if (l >= L && r <= R) {
lazy[rt] += v;
a[rt] += v;
return ;
}
push_down(rt);
if (L <= mid) {
update(lson, L, R, v);
}
if (R > mid) {
update(rson, L, R, v);
}
push_up(rt);
}
int query(int rt, int l, int r, int L, int R) {
if (l >= L && r <= R) {
return a[rt];
}
push_down(rt);
int ans = 0;
if (L <= mid) {
ans = max(ans, query(lson, L, R));
}
if (R > mid) {
ans = max(ans, query(rson, L, R));
}
return ans;
}
}a[20];
int main() {
// freopen("in.txt", "r", stdin);
// freopen("out.txt", "w", stdout);
int n, m;
scanf("%d %d", &n, &m);
char op[10];
for (int i = 1, l, r, x; i <= m; i++) {
scanf("%s %d %d", op, &l, &r);
if (op[1] == 'A') {
printf("ANSWER %d\n", max({a[2].query(1, 1, n, l, r), a[3].query(1, 1, n, l, r), a[5].query(1, 1, n, l, r), a[7].query(1, 1, n, l, r)}));
}
else {
scanf("%d", &x);
int v = 0;
while (x % 2 == 0) {
x /= 2;
v++;
}
if (v) {
a[2].update(1, 1, n, l, r, v);
}
v = 0;
while (x % 3 == 0) {
x /= 3;
v++;
}
if (v) {
a[3].update(1, 1, n, l, r, v);
}
v = 0;
while (x % 5 == 0) {
x /= 5;
v++;
}
if (v) {
a[5].update(1, 1, n, l, r, v);
}
v = 0;
while (x % 7 == 0) {
x /= 7;
v++;
}
if (v) {
a[7].update(1, 1, n, l, r, v);
}
}
}
return 0;
}
I - Base62(进制转换)
import java.math.BigInteger;
import java.util.Stack;
import java.util.Scanner;
public class Main {
private static final String TARGET_STR = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
private static final char[] chs = TARGET_STR.toCharArray();
private static final BigInteger INTEGER0 = new BigInteger("0");
public static String numToRadix(String number, int radix) {
if(radix < 0 || radix > TARGET_STR.length()){
radix = TARGET_STR.length();
}
BigInteger bigNumber = new BigInteger(number);
BigInteger bigRadix = new BigInteger(radix + "");
Stack<Character> stack = new Stack<>();
StringBuilder result = new StringBuilder(0);
while (!bigNumber.equals(INTEGER0)) {
stack.add(chs[bigNumber.remainder(bigRadix).intValue()]);
bigNumber = bigNumber.divide(bigRadix);
}
for (; !stack.isEmpty(); ) {
result.append(stack.pop());
}
return result.length() == 0 ? "0" : result.toString();
}
public static String radixToNum(String number, int radix){
if(radix < 0 || radix > TARGET_STR.length()){
radix = TARGET_STR.length();
}
if (radix == 10) {
return number;
}
char ch[] = number.toCharArray();
int len = ch.length;
BigInteger bigRadix = new BigInteger(radix + "");
BigInteger result = new BigInteger("0");
BigInteger base = new BigInteger("1");
for (int i = len - 1; i >= 0; i--) {
BigInteger index = new BigInteger(TARGET_STR.indexOf(ch[i]) + "");
result = result.add(index.multiply(base)) ;
base = base.multiply(bigRadix);
}
return result.toString();
}
public static String transRadix(String num, int fromRadix, int toRadix) {
return numToRadix(radixToNum(num, fromRadix), toRadix);
}
public static void main(String[] args) {
Scanner cin = new Scanner(System.in);
int x = cin.nextInt();
int y = cin.nextInt();
String s = cin.next();
System.out.println(Main.transRadix(s, x, y));
}
}
K - Largest Common Submatrix(悬线 DP)
给定两个 n × m n \times m n×m的矩阵,找到一个矩阵,在这两个矩阵中都出现过,输出这个矩阵的最大值。
矩阵匹配问题,容易想到用悬线 DP,三个数组 l [ i ] [ j ] , r [ i ] [ j ] , u p [ i ] [ j ] l[i][j], r[i][j], up[i][j] l[i][j],r[i][j],up[i][j],分别记录,从 ( i , j ) (i, j) (i,j)点向左能到哪个位置,向右能到哪个位置,向上能拓展多少格。
#include <bits/stdc++.h>
using namespace std;
const int N = 1e3 + 10;
int a[N][N], b[N][N], l[N][N], r[N][N], up[N][N], X[N * N], Y[N * N], n, m;
int main() {
scanf("%d %d", &n, &m);
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
scanf("%d", &a[i][j]);
l[i][j] = r[i][j] = j, up[i][j] = 1;
}
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
scanf("%d", &b[i][j]);
X[b[i][j]] = i, Y[b[i][j]] = j;
}
}
for (int i = 1; i <= n; i++) {
for (int j = 2; j <= m; j++) {
if (a[i][j - 1] == b[X[a[i][j]]][Y[a[i][j]] - 1]) {
l[i][j] = l[i][j - 1];
}
}
for (int j = m - 1; j >= 1; j--) {
if (a[i][j + 1] == b[X[a[i][j]]][Y[a[i][j]] + 1]) {
r[i][j] = r[i][j + 1];
}
}
}
int ans = 0;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
if (i > 1 && a[i - 1][j] == b[X[a[i][j]] - 1][Y[a[i][j]]]) {
up[i][j] = up[i - 1][j] + 1;
l[i][j] = max(l[i][j], l[i - 1][j]);
r[i][j] = min(r[i][j], r[i - 1][j]);
}
ans = max(ans, (r[i][j] - l[i][j] + 1) * up[i][j]);
}
}
printf("%d\n", ans);
return 0;
}
N - Fibonacci Sequence(签到)
#include <bits/stdc++.h>
using namespace std;
int main() {
// freopen("in.txt", "r", stdin);
// freopen("out.txt", "w", stdout);
puts("1 1 2 3 5");
return 0;
}