VP Educational Codeforces Round 17


A. k-th divisor

题意:找\(n\)的第\(k\)个因子。

数据范围看起来很大,实际上可以暴力找约数,然后排序。

点击查看代码
void solve() {
    i64 n, k;
    std::cin >> n >> k;
    std::vector<i64> a;
    for (i64 i = 1; i * i <= n; ++ i) {
    	if (n % i == 0) {
    		a.push_back(i);
    		if (n % (n / i) == 0 && i * i != n) {
    			a.push_back(n / i);
    		}
    	}
    }

    std::sort(a.begin(), a.end());
    -- k;
    if (k < a.size()) {
    	std::cout << a[k] << "\n";
    } else {
    	std::cout << -1 << "\n";
    }
}

B. USB vs. PS/2

题意:有两种类型的鼠标,以及一些电脑,有些电脑只能用类型一的鼠标,有些只能用类型二的鼠标,有些都可以用。给出\(n\)个鼠标的类型和价格。你要尽可能满足多的电脑,然后让总价值最小。

把两种类型的鼠标按价格分别排序。然后对应类型的鼠标给对应类型的电脑。有多出来的给可以用两种类型的电脑。

点击查看代码
void solve() {
    int a, b, c;
    std::cin >> a >> b >> c;
    int m;
    std::cin >> m;
    std::vector<int> A, B;
    for (int i = 0; i < m; ++ i) {
    	int v;
    	std::string s;
    	std::cin >> v >> s;
    	if (s == "USB") {
    		A.push_back(v);
    	} else {
    		B.push_back(v);
    	}
    }

    std::sort(A.begin(), A.end());
    std::sort(B.begin(), B.end());

    i64 sum = 0;
    int i = 0, j = 0;
    while (i < A.size() && a) {
    	sum += A[i ++ ];
    	-- a;
    }

    while (j < B.size() && b) {
    	sum += B[j ++ ];
    	-- b;
    }

    while ((i < A.size() || j < B.size()) && c) {
    	if (i < A.size() && j < B.size()) {
    		if (A[i] < B[j]) {
    			sum += A[i ++ ];
    		} else {
    			sum += B[j ++ ];
    		}
    	} else if (i < A.size()) {
    		sum += A[i ++ ];
    	} else {
    		sum += B[j ++ ];
    	}

    	-- c;
    }

    std::cout << i + j << " " << sum << "\n";
}

C. Two strings

题意:给你两个字符串\(a, b\),你要删除\(b\)的一个连续子串,使得\(b\)\(a\)的子序列,使得删除的子串的长度最小。

考虑二分,枚举左端点,因为是子序列,那么如果一个长度为\(mid\)的子串删除后可以满足条件,那么比包括它的比它更长的子串也满足条件。但我们的\(check\)只能是\(O(1)\)的,我们回想匹配子序列的过程,就是取每个字符最靠前的位置,那么这满足了能匹配的最后一个位置一定最靠前。
那么我们可以预处理从前往后匹配到\(b_i\)的最小位置\(L_i\),以及从后往前匹配到\(b_i\)的最大位置\(R_i\)。那么对于给一个子串\([l, r]\),只要\(L_{l-1} < R_{r + 1}\)就能满足条件。

点击查看代码
void solve() {
    std::string a, b;
    std::cin >> a >> b;
    int n = a.size(), m = b.size();
    std::vector<int> L(m, n), R(m + 1, -1);
    for (int i = 0, j = 0; i < n; ++ i) {
    	if (a[i] == b[j]) {
    		L[j] = i;
    		++ j;
    	}
    }

    for (int i = n - 1, j = m - 1; i >= 0; -- i) {
    	if (a[i] == b[j]) {
    		R[j] = i;
    		-- j;
    	}
    }

    if (L[m - 1] != n) {
    	std::cout << b << "\n";
    	return;
    }

    auto check = [&](int l, int r) -> bool {
    	if (l == 0) {
    		return r == m - 1 || R[r + 1] != -1;
    	}

    	if (r == m - 1) {
    		return l == 0 || L[l - 1] != n;
    	}

    	return L[l - 1] < R[r + 1];
    };

    int ansl = 0, ansr = m - 1;
    for (int i = 0; i < m; ++ i) {
    	int l = i, r = m - 1;
    	while (l < r) {
    		int mid = l + r >> 1;
    		if (check(i, mid)) {
    			r = mid;
    		} else {
    			l = mid + 1;
    		}
    	}

    	if (check(i, l) && l - i < ansr - ansl) {
    		ansl = i, ansr = l;
    	}
    }

    std::string ans = b.substr(0, ansl) + b.substr(ansr + 1);
    if (ans.empty()) {
    	ans = "-";
    }
    std::cout << ans << "\n";
}

D. Maximum path

题意:一个\(3 \times n\)的矩阵,你可以往上下左右走,求从\((1, 1)\)\((3, n)\)的路径总和最大。

如果不能往左走,我们就可以考虑按列\(dp\)\(f[i][j]\)为在\((i, j)\)处的最大值。但现在可以往左走,我们无法处理这个情况,但手画一下发现,只能在第二行往左走,并且任意一种情况都可以转换为只往左走一步的情况。那么\(f[0][j], f[2][j]\)就加上左边这个\(3 \times 2\)的矩形的转移就行了。

点击查看代码
void solve() {
    int n;
    std::cin >> n;
    std::vector a(3, std::vector<i64>(n + 1));
    for (int i = 0; i < 3; ++ i) {
    	for (int j = 1; j <= n; ++ j) {
    		std::cin >> a[i][j];
    	}
    }

    std::vector f(3, std::vector<i64>(n + 1, -1e18));
    f[0][0] = 0;
    f[0][1] = a[0][1]; f[1][1] = a[0][1] + a[1][1]; f[2][1] = a[0][1] + a[1][1] + a[2][1];
    for (int i = 2; i <= n; ++ i) {
    	f[0][i] = std::max({f[0][i - 1] + a[0][i], 
    						f[1][i - 1] + a[1][i] + a[0][i], 
    						f[2][i - 1] + a[2][i] + a[1][i] + a[0][i]});
    	f[1][i] = std::max({f[0][i - 1] + a[0][i] + a[1][i], 
    						f[1][i - 1] + a[1][i],
    						f[2][i - 1] + a[2][i] + a[1][i]});
    	f[2][i] = std::max({f[0][i - 1] + a[0][i] + a[1][i] + a[2][i], 
    						f[1][i - 1] + a[1][i] + a[2][i],
    						f[2][i - 1] + a[2][i]});

		f[0][i] = std::max(f[0][i], f[2][i - 2] + a[2][i - 1] + a[2][i] + a[1][i] + a[1][i - 1] + a[0][i - 1] + a[0][i]);
		f[2][i] = std::max(f[2][i], f[0][i - 2] + a[0][i - 1] + a[0][i] + a[1][i] + a[1][i - 1] + a[2][i - 1] + a[2][i]);
    }

    std::cout << f[2][n] << "\n";
}

E. Radio stations

题意:有\(n\)个三元组\((x_i, r_i, f_i)\),求满足\(|f_i - f_j| \leq k, x_i - r_i \leq x_j \leq x_i + r_i, x_j - r_j \leq x_i \leq x_j + r_j\)的点对数量。

\(r\)从大到小排序,那么对于第\(i\)个元素,在\([x_i - r_i, x_i + r_i]\)之间的点因为它们的\(r > r_i\),所以这些元素除了\(|f_i - f_j| \leq k\)这个条件都满足了。发现\(f\)\(k\)的值域较小,可以给每一个\(f\)开一棵线段树,然后枚举符合条件的\(f\)的范围的线段树上有多少点满足。用动态开点线段树维护\(x\)的值域。

点击查看代码
#define ls(u) tr[u].lson
#define rs(u) tr[u].rson

const int N = 1e5 + 5;

struct Node {
	int lson, rson;
	int sum;
}tr[N << 5];
int tot = 0;

int creat() {
	++ tot;
	tr[tot].lson = tr[tot].rson = tr[tot].sum = 0;
	return tot;
}

void pushup(int u) {
	tr[u].sum = tr[ls(u)].sum + tr[rs(u)].sum;
}

void insert(int & u, int l, int r, int p) {
	if (u == 0) {
		u = creat();
	}

	if (l == r) {
		tr[u].sum += 1;
		return;
	}

	int mid = l + r >> 1;
	if (p <= mid) {
		insert(ls(u), l, mid, p);
	} else {
		insert(rs(u), mid + 1, r, p);
	}

	pushup(u);
}

int query(int & u, int l, int r, int L, int R) {
	if (u == 0) {
		return 0;
	}

	if (L <= l && r <= R) {
		return tr[u].sum;
	}

	int mid = l + r >> 1;
	if (R <= mid) {
		return query(ls(u), l, mid, L, R);
	} else if (L > mid) {
		return query(rs(u), mid + 1, r, L, R);
	}

	return query(ls(u), l, mid, L, mid) + query(rs(u), mid + 1, r, mid + 1, R);
}

void solve() {
    int n, k;
    std::cin >> n >> k;
    std::vector<std::array<int, 3>> a(n);
    int M = 0;
    for (int i = 0; i < n; ++ i) {
    	int x, r, f;
    	std::cin >> x >> r >> f;
    	a[i] = {r, x, f};
    	M = std::max(M, x);
    }

    std::sort(a.begin(), a.end(), std::greater<>());

    std::vector<int> root(10010);
    i64 ans = 0;
    for (auto & [r, x, f] : a) {
    	for (int i = std::max(1, f - k); i <= std::min(10000, f + k); ++ i) {
    		ans += query(root[i], 1, M, std::max(1, x - r), std::min(x + r, M));
    	}

    	insert(root[f], 1, M, x);
    }

    std::cout << ans << "\n";
}
posted @ 2025-02-26 15:25  maburb  阅读(24)  评论(0)    收藏  举报