XXII Open Cup, GP of Bytedance 【杂题】
A Driverless Car II
给定平面上 \(n\) 个点 \(P_i(x_i,y_i)\) 和正整数 \(k\),求 \(\{P\in\mathbb R^2:\exists i,j\text{ s.t. }i\ne j\land|PP_i|+|PP_j|\le k\}\) 的面积。
\(n\le 2000\),\(k\le 3\cdot 10^4\),\(|x_i|,|y_i|\le 10^4\),对任意 \(i\ne j\) 都有 \(P_i\ne P_j\) 且 \(|k-|P_iP_j||\ge 0.01\),rcmp6.
solution
不会写 Voronoi 图,寄了。计算几何能不能 414。
B Longest Increasing Subsequence
给定长为 \(n\) 的递增正整数序列 \(a_1,\cdots,a_n\),按如下方式生成另一个序列 \(b\):初始令 \(b=a\),不断进行操作,每次操作令 \(s\) 为 \(b\) 排序后的序列,按顺序枚举 \(i=1,\cdots,|s|-1\),若 \(s_i+1<s_{i+1}\) 则在 \(b\) 的末尾加入 \(\lfloor\frac{s_i+s_{i+1}}2\rfloor\),直到不能加为止。求 \(b\) 的 LIS 长度。
\(n\le 10^5\),\(1\le a_1<\cdots<a_n<10^{18}\)。
solution
考虑相邻两个数 \(a_i,a_{i+1}\) 在每次操作中生成的数列 \(S_{i,0},\cdots,S_{i,c_i}\),可知 \(c_i=\lfloor\log_2(a_{i+1}-a_i)\rfloor\),\(j<c_i\) 时 \(|S_{i,j}|=2^j\),\(|S_{i,c_i}|=a_{i+1}-a_i-2^{c_i}\),将 \(j\) 作为另一维将 \(S_{i,j}\) 排在矩阵上,所求即为两维不降的最长子序列长度。进行一个 dp,设 \(f_{i,j}\) 表示考虑 \(\le a_i\) 的数,要求另一维 \(\le j\) 的答案,转移需要计算 \(S_{i,j}+\cdots+S_{i,k}\) 的 LIS 长度:当 \(k<c_i\) 时即为 \(2^k\),否则当 \(j=c_i\) 时显然就是 \(|S_{i,c_i}|\),\(j<c_i\) 时可以由 \(S_{i,c_i-1}\) 与 \(S_{i,c_i}\) 拼一下,可知即为 \(\max(|S_{i,c_i-1}|,|S_{i,c_i}|)+[|S_{i,c_i}|>0]\)。
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 100005, K = 61;
template<typename T>
bool chmax(T &a, const T &b){if(a < b) return a = b, 1; return 0;}
int n, c[N];
LL a[N], f[N][K];
int main(){
ios::sync_with_stdio(0);
cin >> n; -- n;
for(int i = 0;i <= n;++ i) cin >> a[i];
for(int i = n;i;-- i) c[i] = 63 - __builtin_clzll(a[i] -= a[i - 1]);
for(int i = 0;i < K;++ i) f[0][i] = 1;
for(int i = 1;i <= n;++ i){
for(int j = 0;j < c[i];++ j) f[i][j] = f[i - 1][j] + (1ll << j);
f[i][c[i]] = f[i - 1][c[i]] + a[i] - (1ll << c[i]);
if(c[i] && a[i] > (1ll << c[i]))
chmax(f[i][c[i]], f[i - 1][c[i] - 1] + max(1ll << (c[i] - 1), a[i] - (1ll << c[i])) + 1);
for(int j = 0;j < K;++ j)
chmax(f[i][j], f[i - 1][j]);
for(int j = 1;j < K;++ j)
chmax(f[i][j], f[i][j - 1]);
}
cout << f[n][K - 1] << '\n';
}
C New Equipments III
给定左右各 \(n\) 个点的完全二分图,边带非负整数权值,仅有 \(m\) 条边的边权非 \(0\),对每个 \(k=1,\cdots,n\) 求 \(k\) 条边的匹配的权值之和最大值。
\(n\le 5\cdot 10^4\),\(m\le 2\cdot 10^5\),\(w\le 5\)。
solution
考虑 dijkstra 做费用流,但每次增广的时候用 dinic 在最短路 dag 上求最大流,最短路长度是不小于 \(-5\) 的负整数且单增,所以至多跑 \(5\) 次 dinic 就可以了。时间复杂度 \(\mathcal O(5m\sqrt n)\)。
#include<bits/stdc++.h>
using namespace std;
typedef pair<int, int> pii;
template<typename T>
bool chmin(T &a, const T &b){if(a > b) return a = b, 1; return 0;}
const int N = 100005, M = N * 6;
int n, m, S, T, cnt = 1, hd[N], to[M], nxt[M], cap[M], cst[M], h[N], dis[N], ans[6];
void add(int a, int b, int c, int d){
to[++ cnt] = b; nxt[cnt] = hd[a]; hd[a] = cnt; cap[cnt] = c; cst[cnt] = d;
to[++ cnt] = a; nxt[cnt] = hd[b]; hd[b] = cnt; cap[cnt] = 0; cst[cnt] = -d;
}
bool vis[N];
bool dijkstra(){
priority_queue<pii, vector<pii>, greater<pii> > pq;
memset(dis, 0x3f, sizeof(dis));
memset(vis, 0, sizeof(vis));
pq.emplace(dis[S] = 0, S);
while(!pq.empty()){
int u = pq.top().second; pq.pop();
if(vis[u]) continue;
vis[u] = 1;
for(int i = hd[u];i;i = nxt[i]) if(cap[i]){
int v = to[i], w = cst[i] + h[u] - h[v];
if(chmin(dis[v], dis[u] + w) && !vis[v]) pq.emplace(dis[v], v);
}
}
for(int i = 1;i <= T;++ i) h[i] += dis[i];
return h[S] > h[T];
}
int q[N], fr, re, cur[N], dep[N];
bool bfs(){
memcpy(cur, hd, sizeof(cur));
memset(dep, 0x3f, sizeof(dep));
fr = re = 0; dep[q[re ++] = S] = 0;
while(fr < re){
int u = q[fr ++];
for(int i = hd[u];i;i = nxt[i])
if(cap[i] && cst[i] == h[to[i]] - h[u] && chmin(dep[to[i]], dep[u] + 1)) q[re ++] = to[i];
}
return dep[T] < 1e9;
}
int dfs(int x, int lim){
if(x == T || !lim) return lim;
int flow = 0, f;
for(int &i = cur[x];i;i = nxt[i])
if(cap[i] && cst[i] == h[to[i]] - h[x] && dep[to[i]] == dep[x] + 1 && (f = dfs(to[i], min(lim, cap[i])))){
flow += f; lim -= f; cap[i] -= f; cap[i ^ 1] += f; if(!lim) break;
}
return flow;
}
int main(){
ios::sync_with_stdio(0);
cin >> n >> m; S = n * 2 + 1; T = S + 1; h[S] = 5;
for(int i = 1;i <= n;++ i){h[i] = 5; add(S, i, 1, 0); add(i + n, T, 1, 0);}
for(int i = 1, u, v, w;i <= m;++ i){cin >> u >> v >> w; add(u, v + n, 1, -w);}
while(dijkstra()) while(bfs()) ans[h[S] - h[T]] += dfs(S, 1e9);
int now = 0; ans[0] = n;
for(int i = 5;i;-- i){
ans[0] -= ans[i];
for(int j = 0;j < ans[i];++ j)
cout << (now += i) << '\n';
}
for(int j = 0;j < ans[0];++ j)
cout << now << '\n';
}
E Card Shark
#include<bits/stdc++.h>
using namespace std;
const int N = 200003;
int n, m, b, ans[N], tp, deg[N], ps[N];
vector<pair<int, int> > E[N];
void dfs(int x){
while(ps[x] < E[x].size()){
auto [v, id] = E[x][ps[x] ++];
dfs(v); ans[tp ++] = id;
}
}
int main(){
ios::sync_with_stdio(0);
cin >> n >> m >> b; -- b; string str;
for(int i = 1;i <= n;++ i){
cin >> str; int s = 0;
while(s < str.size() && str[s] == '0') ++ s;
if(s >= m){cout << "-1\n"; return 0;}
for(int j = 0;j < str.size();++ j)
if(str[j] - '0' != (j % m == s)){cout << "-1\n"; return 0;}
s = (b - s + m) % m; int nxt = (s + str.size()) % m;
-- deg[s]; ++ deg[nxt]; E[s].emplace_back(nxt, i);
}
for(int i = 0;i < m;++ i) if(deg[i]){cout << "-1\n"; return 0;}
dfs(0); if(tp != n){cout << "-1"; return 0;}
reverse(ans, ans + n);
for(int i = 0;i < n;++ i) cout << ans[i] << " \n"[i == n - 1];
}
F Coprime Matrice
给定正整数 \(n,m,x,y,w\),构造 \(n\times m\) 的矩阵 \(M\) 使得:
- \(M_{x,y}=w\);
- 对于所有 \(1\le i\le nm\),\(i\) 在 \(M\) 中恰好出现一次。
- 对于所有 \(1<i<n\) 和 \(1\le j\le m\),都有 \(\gcd(M_{i-1,j},M_{i,j})=1\) 或 \(\gcd(M_{i,j},M_{i+1,j})=1\) 成立;
- 对于所有 \(1\le i\le n\) 和 \(1<j<m\),都有 \(\gcd(M_{i,j-1},M_{i,j})=1\) 或 \(\gcd(M_{i,j},M_{i,j+1})=1\) 成立。
\(n,m\le 300\),需判断无解。
solution
提到互质就想到相邻数,所以尽可能填相邻数是比较好的,先不管 \(M_{x,y}=w\),观察后两个条件得出以下构造:
1 2 | 11 12 | 21
4 3 | 14 13 | 22
5 6 | 15 16 | 23
8 7 | 18 17 | 24
9 10 | 19 20 | 25
对于 \(M_{x,y}=w\),由于 \(1\) 与 \(nm\) 也是互质的,对值循环平移一下即可。
#include<bits/stdc++.h>
using namespace std;
const int N = 305;
int n, m, x, y, w, a[N][N];
int main(){
ios::sync_with_stdio(0);
cin >> n >> m >> x >> y >> w; -- x; -- y; -- w;
int now = 0, mod = n * m;
for(int j = 1;j < m;j += 2)
for(int i = 0;i < n;++ i)
if(i & 1){a[i][j] = now ++; a[i][j - 1] = now ++;}
else {a[i][j - 1] = now ++; a[i][j] = now ++;}
if(m & 1) for(int i = 0;i < n;++ i) a[i][m - 1] = now ++;
if((now = w - a[x][y]) < 0) now += mod;
cout << "Yes\n";
for(int i = 0;i < n;++ i)
for(int j = 0;j < m;++ j){
if((a[i][j] += now) >= mod) a[i][j] -= mod;
cout << a[i][j] + 1 << " \n"[j == m - 1];
}
}
G Factor
给定正整数 \(n\),求满足下述条件的正整数 \(x\le n\) 的数量:对正整数 \(y\le x\),都有 \(y\) 可以表示为 \(x\) 的不同因数之和。
\(n\le 10^{12}\)。
solution
设 \(x\) 的质因数分解形式为 \(\prod_{i=1}^m p_i^{e_i}\),其中 \(p_i\) 递增,可以发现充要条件即为 \(p_i\le 1+\sigma_1(\prod_{j=1}^{i-1}p_j^{e_j})\)。
然后可以发现 \(p_m\) 不会很大:\(e_m>1\) 时 \(p_m\le\sqrt{n}\),直接爆搜即可;否则设 \(s=\prod_{i=1}^{m-1}p_i^{e_i}\),要求 \(p_{m-1}<p_m\le\min(\sigma_1(s)+1,n/s)\),也是 \(\widetilde{O}(\sqrt n)\) 级别的,线性筛预处理质数个数,然后对 \(s\) 爆搜即可。
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 2111111;
int pri[156666], tot, pre[N];
bool notp[N];
LL n, ans = 2;
void dfs(int d, LL val, LL sum){
if(d == tot || pri[d] > sum + 1) return;
LL tv = 1, lim = n / (val * pri[d] * pri[d]);
while(tv <= lim){++ ans; tv *= pri[d];}
LL ts = 1 + pri[d]; tv = pri[d]; lim = n / (val * pri[d + 1]);
for(int i = 1;tv <= lim;++ i, tv *= pri[d], ts += tv)
ans += pre[min(n / (val * tv), sum * ts + 1)] - d - 1;
ts = 1; tv = 1; lim = n / (val * pri[d + 1] * pri[d + 1]);
for(int i = 0;tv <= lim;++ i, tv *= pri[d], ts += tv) dfs(d + 1, val * tv, sum * ts);
}
int main(){
notp[0] = notp[1] = 1;
for(int i = 2;i < N;++ i){
if(!notp[i]) pri[tot ++] = i; pre[i] = tot;
for(int j = 0;j < tot && i * pri[j] < N;++ j){
notp[i * pri[j]] = 1; if(!(i % pri[j])) break;
}
}
ios::sync_with_stdio(0);
cin >> n; if(n == 1){puts("1"); return 0;}
dfs(0, 1, 1); cout << ans << '\n';
}
H Graph Operation
给定两张 \(n\) 个点 \(m\) 条边的无向简单图 \(G,H\),点编号为 \(1\sim n\),你需要将 \(G\) 变为 \(H\),你可以进行至多 \(3\cdot 10^6\) 次如下操作:选择 \((a,b),(c,d)\in G\) 且 \((a,c),(b,d)\notin G\) 的两两不同的四个点 \(a,b,c,d\),将边 \((a,b),(c,d)\) 删掉,加入边 \((a,c),(b,d)\)。
\(4\le n\le 1000\),需判断无解。
solution
一个显然的必要条件是对应点度数相等,猜想这也是充分的。
依次考虑每个点 \(u\) 在两个图中的邻点,若集合相同就可以不管 \(u\) 了,直接把 \(u\) 删掉。否则设 \((u,v)\in G\backslash H\) 而 \((u,w)\in H\backslash G\),考虑怎么操作:若存在 \(t\) 使得 \((t,v)\notin G\) 而 \((t,w)\in G\),对 \(u,v,w,t\) 操作即可。
\(w\) 的邻点集是 \(v\) 的子集时就寄了,怎么办啊?注意到操作是可逆的,所以也可以对 \(H\) 操作,然后就做完了。使用 bitset 优化,时间复杂度 \(\mathcal O(n^3/w)\)。
#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ULL;
const int N = 1005;
int n, m, deg[N];
struct BS {
ULL a[16];
BS(){memset(a, 0, sizeof(a));}
void set(int p){a[p >> 6] |= 1ull << (p & 63);}
void reset(int p){a[p >> 6] &= ~(1ull << (p & 63));}
bool test(int p) const {return (a[p >> 6] >> (p & 63)) & 1;}
int find() const {for(int i = 0;i < 16;++ i) if(a[i]) return (i << 6) + __builtin_ctzll(a[i]); return -1;}
BS operator ^ (const BS &o) const {BS res; for(int i = 0;i < 16;++ i) res.a[i] = a[i] ^ o.a[i]; return res;}
BS operator & (const BS &o) const {BS res; for(int i = 0;i < 16;++ i) res.a[i] = a[i] & o.a[i]; return res;}
BS operator - (const BS &o) const {BS res; for(int i = 0;i < 16;++ i) res.a[i] = a[i] & ~o.a[i]; return res;}
} G[N], H[N];
vector<array<int, 4> > ag, ah;
void add1(int a, int b, int c, int d){
G[a].reset(b); G[b].reset(a);
G[c].reset(d); G[d].reset(c);
G[a].set(c); G[c].set(a);
G[b].set(d); G[d].set(b);
ag.push_back({a + 1, b + 1, c + 1, d + 1});
}
void add2(int a, int b, int c, int d){
H[a].set(b); H[b].set(a);
H[c].set(d); H[d].set(c);
H[a].reset(c); H[c].reset(a);
H[b].reset(d); H[d].reset(b);
ah.push_back({a + 1, b + 1, c + 1, d + 1});
}
int main(){
ios::sync_with_stdio(0);
cin >> n >> m;
for(int i = 0, u, v;i < m;++ i){
cin >> u >> v;
++ deg[-- u]; ++ deg[-- v];
G[u].set(v); G[v].set(u);
}
for(int i = 0, u, v;i < m;++ i){
cin >> u >> v;
-- deg[-- u]; -- deg[-- v];
H[u].set(v); H[v].set(u);
}
for(int i = 0;i < n;++ i) if(deg[i]){cout << "-1\n"; return 0;}
for(int u = 0;u < n;++ u){
while(true){
int v = (G[u] - H[u]).find(), w = (H[u] - G[u]).find();
if(v == -1 || w == -1) break;
BS tmp = G[w] - G[v]; tmp.reset(v); int t = tmp.find();
if(~t) add1(u, v, w, t);
else {
tmp = H[v] - H[w]; tmp.reset(w);
add2(u, v, w, tmp.find());
}
}
for(int i = 0;i < n;++ i){
G[i].reset(u); H[i].reset(u);
G[u].reset(i); H[u].reset(i);
}
}
reverse(ah.begin(), ah.end());
cout << ag.size() + ah.size() << '\n';
for(auto [a, b, c, d] : ag) cout << a << ' ' << b << ' ' << c << ' ' << d << '\n';
for(auto [a, b, c, d] : ah) cout << a << ' ' << b << ' ' << c << ' ' << d << '\n';
}
I Optimal Assortment
给定长为 \(n\) 的整数序列 \(v_i,l_i,r_i\),支持 \(m\) 次单点修改并查询下述式子的值:
\(n,m\le 2\times 10^5\),\(1\le v_i\le 10^6\),\(0\le l_i\le r_i\le 10^6\)。
solution
显然 \(w_0=r_0\),其他的 \(w_i=l_i\),考虑二分,答案不小于 \(\text{ans}\) 就是说 \(\sum_{i\in S}w_i(v_i-\text{ans})\ge w_0\cdot\text{ans}\),所以 \(S\) 即为所有 \(v_i\ge\text{ans}\) 的物品,使用线段树维护并在线段树上二分即可。
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
LL gcd(LL a, LL b){return b ? gcd(b, a % b) : a;}
const int N = 200003, M = 1 << 21, V = 1e6;
int n, m, v[N], w[N], chisato_very_cute;
LL smw[M], smp[M];
void upd(int p, int v, int x = 1, int L = 1, int R = V){
smw[x] += v; smp[x] += (LL)v * p;
if(L == R) return;
int md = L + R >> 1;
if(p <= md) upd(p, v, x << 1, L, md);
else upd(p, v, x << 1 | 1, md + 1, R);
}
void calc(){
LL tmp1 = 0, tmp2 = w[0];
int x = 1, L = 1, R = V;
while(L < R){
int md = L + R >> 1, rs = x << 1 | 1;
if(tmp1 + smp[rs] >= max((tmp2 + smw[rs]) * md, 1ll)){L = md + 1; x = rs;}
else {tmp1 += smp[rs]; tmp2 += smw[rs]; R = md; x <<= 1;}
}
tmp1 += smp[x]; tmp2 += smw[x];
if(tmp1 || tmp2){LL g = gcd(tmp1, tmp2); cout << tmp1 / g << '/' << tmp2 / g << '\n';}
else cout << "0/1\n";
}
int main(){
ios::sync_with_stdio(0);
cin >> n >> m;
for(int i = 1;i <= n;++ i) cin >> v[i];
cin >> chisato_very_cute;
for(int i = 1;i <= n;++ i){cin >> w[i]; upd(v[i], w[i]);}
cin >> w[0];
for(int i = 1;i <= n;++ i) cin >> chisato_very_cute;
calc(); int op, x, y;
while(m --){
cin >> op >> x >> y;
if(op == 1){
cin >> (x ? chisato_very_cute : y);
upd(v[x], y - w[x]); w[x] = y;
} else {
upd(v[x], -w[x]); upd(v[x] = y, w[x]);
} calc();
}
}