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\) 次单点修改并查询下述式子的值:

\[\max\left\{\min\left\{\frac{\sum_{i\in S}w_iv_i}{w_0+\sum_{i\in S}w_i}:w_i\in[l_i,r_i]\right\}:S\subset[n]\right\} \]

\(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();
	}
}
posted @ 2022-07-30 00:51  mizu164  阅读(345)  评论(1编辑  收藏  举报