题目链接
https://codeforces.com/gym/102801
参考题解
B - Team
简要题意:
给定 n n n 和 M M M,有三个组 A B C ABC ABC,每组 n n n 个人,每人都有一个能力值 v i v_i vi,定义 f ( v i , v j ) = ( v i + v j ) ∗ ( v i ⊕ v j ) % M f(v_i, v_j) = (v_i + v_j) * (v_i \oplus v_j) \% M f(vi,vj)=(vi+vj)∗(vi⊕vj)%M,一个队伍由三个不同组的人 a b c abc abc 组成,其能力值和为 f ( a , b ) + f ( a , c ) f(a, b) + f(a,c) f(a,b)+f(a,c),问选出 m m m 个队伍的能力值之和的最大值。
解题思路:
如果只有两组,就是二分图找 m m m 个匹配使得权值和最大,费用流可解。注意到一个队伍的能力值为 f ( a , b ) + f ( a , c ) f(a, b) + f(a, c) f(a,b)+f(a,c),相当于一个 a a a 需要分别匹配 b b b 和 c c c,贡献是独立的,故将 a a a 放在中间, b b b 放左部、 c c c 放右部,每个点只用一次,拆点解决,对应边权计算出来,源点连所有 b b b 点, c c c 点连汇点 T T T,在源点/汇点处限制总流量为 m m m 即可。
参考代码:
#include<bits/stdc++.h>
using namespace std;
#define pb emplace_back
#define sz(a) ((int)a.size())
#define lson (rt << 1)
#define rson (rt << 1 | 1)
#define gmid (l + r >> 1)
typedef long long ll;
typedef pair<int, int> pii;
const int maxn = 1e5 + 5;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
int a[maxn], b[maxn], c[maxn];
int n, m, M;
struct Dinic{
struct Edge{
int v, rev, cap, cot;
};
vector<Edge> G[maxn];
int dis[maxn], cur[maxn], vis[maxn], n, sp, tp;
void init(int nn){
n = nn;
for(int i = 1; i <= n; ++i) G[i].clear();
}
void add(int u, int v, int cap, int cot){
G[u].pb(Edge{v, sz(G[v]), cap, cot});
G[v].pb(Edge{u, sz(G[u]) - 1, 0, -cot});
}
int bfs(){
queue<int> q;
for(int i = 1; i <= n; ++i) dis[i] = inf, vis[i] = 0;
dis[sp] = 0, q.push(sp), vis[sp] = 1;
while(!q.empty()){
int u = q.front(); q.pop(); vis[u] = 0;
for(auto &e : G[u]){
if(e.cap && dis[e.v] > dis[u] + e.cot){
dis[e.v] = dis[u] + e.cot;
if(!vis[e.v]) q.push(e.v), vis[e.v] = 1;
}
}
}
return dis[tp] != inf;
}
int dfs(int u, int flow){
if(u == tp || !flow) return flow;
int ret = 0, tmp; vis[u] = 1;
for(int &i = cur[u]; i < sz(G[u]); ++i){
auto &e = G[u][i];
if(!vis[e.v] && dis[e.v] == dis[u] + e.cot && (tmp = dfs(e.v, min(e.cap, flow - ret)))){
ret += tmp, e.cap -= tmp, G[e.v][e.rev].cap += tmp;
if(ret == flow) { vis[u] = 0; return ret; }
}
}
if(!ret) vis[u] = 1;
return ret;
}
int solve(int s, int t){
sp = s, tp = t;
int ret = 0;
while(bfs()){
for(int i = 1; i <= n; ++i) cur[i] = 0;
ret += dfs(sp, inf) * dis[tp];
}
return ret;
}
} dn;
int main(){
ios::sync_with_stdio(0); cin.tie(0);
int T; cin >> T;
while(T--){
cin >> n >> m >> M;
for(int i = 1; i <= n; ++i) cin >> a[i];
for(int i = 1; i <= n; ++i) cin >> b[i];
for(int i = 1; i <= n; ++i) cin >> c[i];
int S = 4 * n + 1, SS = S + 1, T = SS + 1;
auto cal = [](int x, int y){
return (x + y) * (x ^ y) % M;
};
dn.init(T);
dn.add(SS, S, m, 0);
for(int i = 1; i <= n; ++i){
dn.add(S, i, 1, 0);
dn.add(3 * n + i, T, 1, 0);
dn.add(n + i, 2 * n + i, 1, 0);
for(int j = 1; j <= n; ++j){
dn.add(j, n + i, 1, -cal(a[i], b[j]));
dn.add(2 * n + i, 3 * n + j, 1, -cal(a[i], c[j]));
}
}
int ret = -dn.solve(SS, T);
cout << ret << "\n";
}
return 0;
}
C - Function
简要题意:
定义
f
(
x
)
=
∏
i
=
1
l
e
n
(
x
%
1
0
i
)
%
(
x
+
1
)
f(x) = \prod\limits_{i = 1}^{len}(x\%10^i)\%(x + 1)
f(x)=i=1∏len(x%10i)%(x+1),其中
l
e
n
len
len 是
x
x
x 十进制位数。
定义
g
(
n
,
m
)
=
{
f
(
g
(
n
,
m
−
1
)
)
m
>
1
f
(
n
)
m
=
1
g(n, m) = \left\{\begin{aligned}&f(g(n, m - 1)) & m \gt 1\\ &f(n) & m = 1\end{aligned}\right.
g(n,m)={f(g(n,m−1))f(n)m>1m=1,给定
n
n
n 和
m
m
m,求
∑
i
=
1
m
g
(
n
,
i
)
\sum\limits_{i = 1}^{m} g(n, i)
i=1∑mg(n,i)。
解题思路:
打表发现经过有限次 f f f 后 n = f ( n ) n = f(n) n=f(n),然后就是暴力模拟了。
参考代码:
#include<bits/stdc++.h>
using namespace std;
#define pb emplace_back
#define sz(a) ((int)a.size())
#define lson (rt << 1)
#define rson (rt << 1 | 1)
#define gmid (l + r >> 1)
typedef long long ll;
typedef pair<int, int> pii;
const int maxn = 2e5 + 5;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
ll f(ll n){
if(n == 0) return 0;
ll ret = 1, pw = 10;
while(n >= pw / 10){
ll x = n % pw;
(ret *= x) %= (n + 1);
pw *= 10;
}
return ret;
}
int main(){
ios::sync_with_stdio(0); cin.tie(0);
int T; cin >> T;
while(T--){
ll n, m; cin >> n >> m;
ll ret = 0, pn = n;
for(int i = 1; i <= m; ++i){
n = f(n);
// cout << i << " ?? " << n << endl;
if(pn == n) { ret += (m - i + 1) * n; break; }
ret += n, pn = n;
}
cout << ret << "\n";
}
return 0;
}
D - Fall Guys
简要题意:
给定 n n n 个糖豆人到达皇冠下的时间 x i x_i xi,皇冠在 0 0 0 时刻高度为 0 0 0,单位时间上升 1 1 1 个单位的高度,到达 H H H 后立刻下降,下降到 0 0 0 后又上升,如此反复。糖豆人 i i i 在到达时等待皇冠高度小于等于 h h h 时立即起跳抓皇冠,系统对判定时间延迟 c i c_i ci 个单位。问哪个糖豆人取得胜利,相同时间抓到输出编号最小的。
解题思路:
分类讨论下糖豆人到达皇冠底下时皇冠的移动情况和高度,计算出每个糖豆人实际起跳的时间点,再加上延迟,就是抓到皇冠的时间,取最小值即可。
参考代码:
#include<bits/stdc++.h>
using namespace std;
#define pb emplace_back
#define sz(a) ((int)a.size())
#define lson (rt << 1)
#define rson (rt << 1 | 1)
#define gmid (l + r >> 1)
typedef long long ll;
typedef pair<int, int> pii;
const int maxn = 2e5 + 5;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
pii a[maxn];
int xi[maxn], ci[maxn];
int n, h, H;
int main(){
ios::sync_with_stdio(0); cin.tie(0);
int T; cin >> T;
while(T--){
cin >> n >> h >> H;
for(int i = 1; i <= n; ++i) cin >> xi[i];
for(int i = 1; i <= n; ++i) cin >> ci[i];
auto getH = [](int t) -> pii{
if((t / H) & 1) return pii{-1, H - t % H};
else return pii{1, t % H};
};
for(int i = 1; i <= n; ++i){
auto e = getH(xi[i]);
int t = xi[i] + ci[i];
if(e.second > h){
if(e.first == -1) t += e.second - h;
else t += H - e.second + (H - h);
}
a[i] = {t, i};
}
cout << (*min_element(a + 1, a + 1 + n)).second << endl;
}
return 0;
}
E - Liner vectors
简要题意:
给定 n n n 和 k k k,构造 n n n 个 n n n 维 01 01 01 向量,满足这 n n n 个向量之间线性无关,且每个向量,其各维度的取值之和为 k k k,无解输出 − 1 -1 −1,有多解则输出字典序最小的解。
解题思路:
记构造的向量组为矩阵
A
A
A 的行向量,即要满足
A
A
A 的秩为
n
n
n,且
∀
i
∈
[
1
,
n
]
,
∑
j
=
1
n
a
i
j
=
k
\forall i \in [1, n],\sum\limits_{j = 1}^{n} a_{ij} = k
∀i∈[1,n],j=1∑naij=k。当
n
=
k
n = k
n=k 时,显然无解;当
k
%
2
=
0
k~\%~2 = 0
k % 2=0 时,
A
A
A 的列向量
⨁
j
=
1
n
a
j
=
0
\bigoplus\limits_{j = 1}^{n} \bold{a_j} = \bold{0}
j=1⨁naj=0,显然也不可能有解;否则,以
k
=
3
,
n
=
5
k = 3, n = 5
k=3,n=5 为例,构造的矩阵
A
A
A 为:
A
=
[
0
0
1
1
1
0
1
0
1
1
0
1
1
0
1
0
1
1
1
0
1
0
0
1
1
]
A = \left[\begin{matrix} 0 & 0 & 1 & 1 & 1 \\ 0 & 1 & 0 & 1 & 1 \\ 0 & 1 & 1 & 0 & 1 \\ 0 & 1 & 1 & 1 & 0 \\ 1 & 0 & 0 & 1 & 1 \end{matrix}\right]
A=⎣⎢⎢⎢⎢⎡0000101110101101101111101⎦⎥⎥⎥⎥⎤
即在前
k
+
1
k + 1
k+1 维构造前
k
+
1
k + 1
k+1 个向量,后面第
i
∈
[
k
+
2
,
n
]
i \in [k + 2, n]
i∈[k+2,n] 的向量各在第
i
i
i 维上占有一个
1
1
1。
参考代码:
#include<bits/stdc++.h>
using namespace std;
#define pb emplace_back
#define sz(a) ((int)a.size())
#define lson (rt << 1)
#define rson (rt << 1 | 1)
#define gmid (l + r >> 1)
typedef long long ll;
typedef pair<int, int> pii;
const int maxn = 1e3 + 5;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
int n, k;
int main(){
ios::sync_with_stdio(0); cin.tie(0);
int T; cin >> T;
while(T--){
cin >> n >> k;
if(n == 1) cout << "1\n";
else if(n == k || k % 2 == 0) cout << "-1\n";
else{
cout << (1ll << k) - 1;
for(int i = k - 1; i >= 0; --i){
ll x = (1ll << (k + 1)) - 1;
x ^= 1ll << i;
cout << " " << x;
}
for(int i = k + 2; i <= n; ++i){
ll x = 1ll << (i - 1);
x ^= (1ll << (k - 1)) - 1;
cout << " " << x;
}
cout << "\n";
}
}
return 0;
}
G - Halli Galli
简要题意:
德国心脏病,签到题。
解题思路:
签到题。
参考代码:
#include<bits/stdc++.h>
using namespace std;
#define pb emplace_back
#define sz(a) ((int)a.size())
#define lson (rt << 1)
#define rson (rt << 1 | 1)
#define gmid (l + r >> 1)
typedef long long ll;
typedef pair<int, int> pii;
const int maxn = 2e5 + 5;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
pii a[maxn];
int has[256];
int n, k;
int main(){
ios::sync_with_stdio(0); cin.tie(0);
has['A'] = 0, has['B'] = 1, has['G'] = 2, has['P'] = 3;
int T; cin >> T;
while(T--){
cin >> n >> k;
for(int i = 0; i < k; ++i) a[i] = {0, 0};
auto check = []() -> int{
int cnt[4] = {};
for(int i = 0; i < k; ++i){
cnt[a[i].first] += a[i].second;
}
int ret = 0;
for(int i = 0; i < 4; ++i){
ret += cnt[i] == 5;
}
return ret;
};
int ret = 0, cur = 0;
for(int i = 1; i <= n; ++i){
char ch; int x; cin >> ch >> x;
a[cur] = pii{has[ch], x};
ret += check();
cur = (cur + 1) % k;
}
cout << ret << endl;
}
return 0;
}
H - PepperLa’s String
简要题意:
给一个字符串 s s s,可以删去一个字符,并且可以对其进行任意的压缩表示一个字符加其连续出现次数的十六进制表示,求最短的表示串,相同长度则输出字典序最小的。
解题思路:
如果连续出现两个或以上的字符,压缩表示长度更短,如果只有一个,则显然不需要压缩。考虑删去一个字符使得长度减小的情况:① 删去单一字符,长度减一;② 删去两个字符中的一个,长度减一;③ 删去一个字符后,其出现次数位数减少一。从左到右扫描字符串,如果有 ① 情况且字典序能变小,直接停止;否则剩余能减小长度的情况都会增大字典序(除了在结尾处删),则尽可能让删除位置靠右。不能减小长度的情况,则删去第一个字符最优。
参考代码:
#include<bits/stdc++.h>
using namespace std;
#define pb emplace_back
#define sz(a) ((int)a.size())
#define lson (rt << 1)
#define rson (rt << 1 | 1)
#define gmid (l + r >> 1)
typedef long long ll;
typedef pair<int, int> pii;
const int maxn = 1e6 + 5;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
string s, ch("0123456789ABCDEF");
struct Node{
char ch; string v; int cnt;
} a[maxn];
int n, m;
string getS(int x){
string s = "";
while(x > 0){
s += ch[x % 16];
x /= 16;
}
reverse(s.begin(), s.end());
return s;
};
void print(){
for(int i = 0; i < m; ++i){
if(!a[i].cnt) continue;
else if(a[i].cnt == 1) cout << a[i].ch;
else cout << a[i].ch << a[i].v;
}
cout << "\n";
}
void solve(){
int ret = 0;
for(int i = 0; i < m; ++i){
if(a[i].cnt == 1){
ret = i;
if(i < m && a[i + 1].ch < a[i].ch) break;
}
else if(a[i].cnt == 2) ret = i;
else if(a[i].v == "1" + string(a[i].v.size() - 1, '0')) ret = i;
}
--a[ret].cnt, a[ret].v = getS(a[ret].cnt);
print();
}
int main(){
ios::sync_with_stdio(0); cin.tie(0);
while(cin >> s){
n = s.size(), m = 0;
int cnt = 1, flg = 0;
for(int i = 1; i < n; ++i){
if(s[i] == s[i - 1]) ++cnt;
else a[m++] = Node{s[i - 1], getS(cnt), cnt}, cnt = 1;
}
a[m++] = Node{s[n - 1], getS(cnt), cnt};
solve();
}
return 0;
}
I - PepperLa’s Cram School
简要题意:
给定 n n n 个顶点的完全图的最短距离矩阵,每条边边权相同,保证输入合法,求最少需要使用多少条边满足给定的最短距离矩阵。
解题思路:
e ( u , v ) e(u, v) e(u,v) 如果存在,则 d i s ( u , v ) = w ( u , v ) dis(u, v) = w(u, v) dis(u,v)=w(u,v),否则 d i s ( u , v ) > w ( u , v ) dis(u, v) \gt w(u, v) dis(u,v)>w(u,v) 故数出现的最短距离共有几次即可。
参考代码:
#include<bits/stdc++.h>
using namespace std;
#define pb emplace_back
#define sz(a) ((int)a.size())
#define lson (rt << 1)
#define rson (rt << 1 | 1)
#define gmid (l + r >> 1)
typedef long long ll;
typedef pair<int, int> pii;
const int maxn = 1e3 + 5;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
int a[maxn][maxn];
int n;
int main(){
ios::sync_with_stdio(0); cin.tie(0);
while(cin >> n){
int mn = inf;
for(int i = 1; i <= n; ++i){
for(int j = 1; j <= n; ++j){
cin >> a[i][j];
if(i != j) mn = min(mn, a[i][j]);
}
}
int ret = 0;
for(int i = 1; i <= n; ++i){
for(int j = i + 1; j <= n; ++j){
ret += a[i][j] == mn;
}
}
cout << ret << "\n";
}
return 0;
}
J - Color the blocks
简要题意:
对 n × n n×n n×n 的网格进行黑白染色,需要满足 c ( x , y ) c(x, y) c(x,y) 与 c ( x − 3 , y ) , c ( x + 3 , y ) , c ( x − 1 , y + 2 ) , c ( x + 1 , y − 2 ) c(x - 3, y), c(x + 3, y), c(x - 1, y + 2), c(x + 1, y - 2) c(x−3,y),c(x+3,y),c(x−1,y+2),c(x+1,y−2) 都不同,问染色方案数。
解题思路:
特判 n ≤ 3 n \le 3 n≤3,当 n ≥ 4 n \ge 4 n≥4 时,同一列 j j j 中黑白相间染色,第 j j j 与 j + 2 j + 2 j+2 列染色相同,只有 4 4 4 种可能。
参考代码:
#include<bits/stdc++.h>
using namespace std;
#define pb emplace_back
#define sz(a) ((int)a.size())
#define lson (rt << 1)
#define rson (rt << 1 | 1)
#define gmid (l + r >> 1)
typedef long long ll;
typedef pair<int, int> pii;
const int maxn = 2e5 + 5;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
int main(){
ios::sync_with_stdio(0); cin.tie(0);
int T; cin >> T;
while(T--){
int n; cin >> n;
if(n == 1) cout << "2\n";
else if(n == 2) cout << "16\n";
else if(n == 3) cout << "32\n";
else cout << "4\n";
}
return 0;
}
L - PepperLa’s Express
简要题意:
给一个三维的方格图,每个格子是空地、邮局或居民,现在需要设立一个邮局,使得居民中到最近邮局的最长距离最短,问该值为多少,距离定义为曼哈顿距离。
解题思路:
二分答案,枚举空地判定是否最长距离小于等于二分值, d ( p i , p j ) = ∣ x i − x j ∣ + ∣ y i − y j ∣ + ∣ z i − z j ∣ d(p_i, p_j) = \vert x_i - x_j\vert + \vert y_i - y_j\vert + \vert z_i - z_j\vert d(pi,pj)=∣xi−xj∣+∣yi−yj∣+∣zi−zj∣,利用 ∣ x i − x j ∣ = max { x i − x j , x j − x i } \vert x_i - x_j\vert = ~\max\{~x_i - x_j, x_j - x_i~\} ∣xi−xj∣= max{ xi−xj,xj−xi },将距离展开为 8 8 8 项取最值,故可以预处理出居民对应坐标的最值,每个空地仅需要常数时间来判定。
参考代码:
#include<bits/stdc++.h>
using namespace std;
#define pb emplace_back
#define sz(a) ((int)a.size())
#define lson (rt << 1)
#define rson (rt << 1 | 1)
#define gmid (l + r >> 1)
typedef long long ll;
typedef pair<int, int> pii;
const int maxn = 1e2 + 5;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
const int zp[] = {-1, 1, 0, 0, 0, 0};
const int xp[] = {0, 0, 0, 0, 1, -1};
const int yp[] = {0, 0, 1, -1, 0, 0};
struct Node{
int z, x, y;
};
char a[maxn][maxn][maxn];
int dis[maxn][maxn][maxn];
int Z, X, Y;
void bfs(){
queue<Node> que;
for(int i = 1; i <= Z; ++i){
for(int j = 1; j <= X; ++j){
for(int k = 1; k <= Y; ++k){
if(a[i][j][k] == '@') dis[i][j][k] = 0, que.push(Node{i, j, k});
else dis[i][j][k] = inf;
}
}
}
auto check = [](int z, int x, int y){
return z >= 1 && z <= Z && x >= 1 && x <= X && y >= 1 && y <= Y;
};
while(!que.empty()){
auto now = que.front(); que.pop();
for(int i = 0; i < 6; ++i){
auto nxt = now;
nxt.z += zp[i], nxt.x += xp[i], nxt.y += yp[i];
if(check(nxt.z, nxt.x, nxt.y) && dis[nxt.z][nxt.x][nxt.y] > dis[now.z][now.x][now.y] + 1){
dis[nxt.z][nxt.x][nxt.y] = dis[now.z][now.x][now.y] + 1;
que.push(nxt);
}
}
}
}
int judge(int mid){
int mx[2][2][2] = {};
memset(mx, -0x3f, sizeof mx);
auto cal = [&](int z, int x, int y){
for(int i = 0; i < 2; ++i){
for(int j = 0; j < 2; ++j){
for(int k = 0; k < 2; ++k){
int val = z * (i ? 1 : -1) + x * (j ? 1 : -1) + y * (k ? 1 : -1);
mx[i][j][k] = max(mx[i][j][k], val);
}
}
}
};
int cnt = 0;
for(int i = 1; i <= Z; ++i){
for(int j = 1; j <= X; ++j){
for(int k = 1; k <= Y; ++k){
if(a[i][j][k] != '*' || dis[i][j][k] <= mid) continue;
++cnt;
cal(i, j, k);
}
}
}
if(!cnt) return 1;
auto check = [&](int z, int x, int y) -> int{
int ret = 0;
for(int i = 0; i < 2; ++i){
for(int j = 0; j < 2; ++j){
for(int k = 0; k < 2; ++k){
int val = z * (i ? 1 : -1) + x * (j ? 1 : -1) + y * (k ? 1 : -1);
ret = max(ret, val + mx[i ^ 1][j ^ 1][k ^ 1]);
}
}
}
return ret <= mid;
};
for(int i = 1; i <= Z; ++i){
for(int j = 1; j <= X; ++j){
for(int k = 1; k <= Y; ++k){
if(a[i][j][k] == '.' && check(i, j, k)) return 1;
}
}
}
return 0;
}
int main(){
ios::sync_with_stdio(0); cin.tie(0);
while(cin >> Z >> X >> Y){
for(int i = 1; i <= Z; ++i){
for(int j = 1; j <= X; ++j){
cin >> a[i][j] + 1;
}
}
bfs();
int l = 0, r = Z + X + Y, mid, ret = -1;
while(l <= r){
mid = gmid;
if(judge(mid)) ret = mid, r = mid - 1;
else l = mid + 1;
}
cout << ret << "\n";
}
return 0;
}