2024CCPC山东省赛VP + 补题
solved 7 problems.
A、C、D、F、I、J、K:@dbywsc 、@xmjlove
A.Printer
思路 && 代码:@xmjlove
思路
待补充
代码
public static void main(String[] args) throws Exception {
int t = read();
while (t-- > 0) {
int n = read();
int k = read();
int[][] arr = new int[n][3];
for (int i = 0; i < n; i++) {
arr[i][0] = read();
arr[i][1] = read();
arr[i][2] = read();
}
long l = 0;
long r = 7100000000000000000L;
while (l < r) {
long mid = (l + r) / 2;
if (check(mid, arr, n, k)) {
r = mid;
} else {
l = mid + 1;
}
}
pw.println(l);
}
pw.flush();
}
public static boolean check (long mid, int[][] arr, int n, int k) {
long res = 0;
for (int i = 0; i < n; i++) {
res += mid / ((long) arr[i][0] * arr[i][1] + arr[i][2]) * arr[i][1];
res += Math.min(arr[i][1], mid % ((long) arr[i][0] * arr[i][1] + arr[i][2]) / arr[i][0]);
if (res >= 1000000000) {
return true;
}
}
//pw.println(mid + " " + res);
return res >= k;
}
C.Colorful Segments 2
思路: 赛后补题
代码:@dbywsc
思路
将一个区间存为两个点,用下面的形式表示:
\(\{l_i, 1\}, \{r_i, -1\}\)
然后对所有的点按照坐标升序排序,如果坐标一样就让 \(1\) 排到 \(-1\) 的前面。
接下来遍历所有的点,维护一个变量 \(cnt\) 记录当前区间内重合了几条线段,每次读入标记为 \(1\) 的点时,相当于当前区间内又有了一条重合的线段, \(cnt += 1\) ,所以当前区间内一共有 \(k - cnt\) 种染色方案;否则,说明读到了之前某条线段的末尾, \(cnt -= 1\) 。
代码
void solve(void) {
int n, k; std::cin >> n >> k;
std::vector<PII> a;
for(int i = 1; i <= n; i++) {
int x, y; std::cin >> x >> y;
a.emplace_back(x, 1);
a.emplace_back(y, -1);
}
std::sort(a.begin(), a.end(), [&](PII x, PII y) {
if(x.x == y.x) return x.y > y.y;
return x.x < y.x;
});
int cnt = 0;
i64 ans = 1;
for(auto [x, y] : a) {
if(y == 1) {
ans = ans * (k - cnt) % P;
cnt++;
}
if(y == -1) cnt--;
}
std::cout << ans << '\n';
}
D.Hero of the Kingdom
思路 && 代码:@xmjlove
思路
待补充
代码
待补充
F.Divide the Sequence
思路:@xmjlove
代码:@dbywsc
思路
注意到 \(k = 1\) 时,答案为 \(\sum a_i\) 。否则,答案为 \(\sum a_i\) 加上前 \(k - 1\) 大后缀和之和。
代码
void solve(void) {
int n; std::cin >> n;
std::vector<int> a(n + 1);
std::vector<i64> s(n + 1);
for(int i = 1; i <= n; i++) {
std::cin >> a[i];
}
s[n] = a[n];
for(int i = n - 1; i >= 0; i--) {
s[i] = s[i + 1] + a[i];
}
std::cout << s[1] << ' ';
std::sort(s.begin() + 2, s.end(), std::greater<i64>());
i64 ans = s[1];
for(int i = 2; i <= n; i++) {
ans += s[i];
std::cout << ans << ' ';
}
std::cout << '\n';
}
I.Left Shifting
思路 && 代码:@dbywsc
思路
签到题,直接拿两个指针遍历一遍即可。
代码
void solve(void) {
std::string s;
std::cin >> s;
int l = 0, r = (int)s.size() - 1, ans = 0;
s = s + s;
while(s[l] != s[r]) {
ans++;
l++;
r++;
if(r > (int)s.size() - 1 && s[l] != s[r]) {
std::cout << "-1\n";
return;
}
}
std::cout << ans << '\n';
}
J.Colorful Spanning Tree
思路:@xmjlove
代码:@dbywsc
思路
将相同颜色的点看作一个连通块,将连通块之间两两连边,记颜色 \(i\) 所在的连通块大小为 \(size_i\) ,分情况讨论:
对于相同颜色的点相连,直接加 \(size_i -1\) 条边,然后合并相同颜色的所有点并标记。
对于不同颜色的点相连,直接对两个连通块操操作:
- 如果两个连通块内部还没有自行相连,那么应该加上 \(size_u - 1 + size_v\) 条边;
- 如果两个连通块其中一个自行相连,另一个没有相连,那么应该加上 \(size_{没有合并的连通块}\) 条边;
- 如果两个连通块都已经自行相连,那么加上 \(1\) 条边就行。
操作后合并两个连通块并标记。
由于要求最小生成树,所以做上面的操作前我们需要对边按边权排序,维护合并的操作需要使用并查集,所以这道题其实就是一个缩点思想的 Kruskal 。
代码
struct Edge {
int u, v, w;
};
int b[N][N];
struct DSU {
std::vector<int> p, siz;
DSU(int n): p(n + 1), siz(n + 1, 1) {
std::iota(p.begin(), p.end(), 0);
}
int find(int x) {
if(x == p[x]) return p[x];
return p[x] = find(p[x]);
}
void unite(int a, int b) {
int pa = find(a), pb = find(b);
if(pa == pb) return;
if(siz[pa] < siz[pb]) std::swap(pa, pb);
p[pb] = pa;
siz[pa] += siz[pb];
}
bool same(int u, int v) {return find(u) == find(v);}
int size(int u) {return siz[find(u)];}
};
void solve(void) {
int n; std::cin >> n;
std::vector<int> a(n + 1);
std::vector<Edge> e;
memset(b, 0, sizeof(b));
for(int i = 1; i <= n; i++) std::cin >> a[i];
auto c = a;
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= n; j++) {
int x; std::cin >> x;
if(j < i) continue;
e.push_back({i, j, x});
}
}
std::sort(e.begin(), e.end(), [&](Edge x, Edge y) {
return x.w < y.w;
});
DSU dsu(n);
std::vector<int> st(n + 1);
i64 ans = 0;
for(auto i : e) {
if(i.u == i.v && !st[i.u]) {
st[i.u] = 1;
ans += (a[i.u] - 1) * i.w;
} else {
if(dsu.same(i.u, i.v)) continue;
if(!st[i.u] && !st[i.v]) {
ans += (a[i.u] - 1 + a[i.v]) * i.w;
st[i.u] = 1;
st[i.v] = 1;
dsu.unite(i.u, i.v);
} else if(!st[i.u] && st[i.v]) {
ans += a[i.u] * i.w;
st[i.u] = 1;
dsu.unite(i.u, i.v);
}else if(st[i.u] && !st[i.v]) {
ans += a[i.v] * i.w;
st[i.v] = 1;
dsu.unite(i.u, i.v);
} else if(st[i.u] && st[i.v]) {
ans += i.w;
dsu.unite(i.u, i.v);
}
}
}
std::cout << ans << '\n';
}
K.Matrix
思路:@dbywsc
代码:@xmjlove
思路
注意到形如下面的构造是可行的:
1 2 5 7 ... 2n - 1
3 4 5 7 ... 2n - 1
5 5 6 7 ... 2n - 1
7 7 7 8 ... ....
.. .. .. .. 2n - 1
2n - 1 2n -1 ... 2n - 1 2n
并且由于 \(n \geq 2\) , 不存在无解的情况。
代码
public static void main(String[] args) throws Exception {
int n = read();
int[][] a = new int[n][n];
pw.println("Yes");
if (n == 2) {
pw.println(1 + " " + 2);
pw.println(3 + " " + 4);
} else {
a[0][0] = 1;
a[0][1] = 2;
a[1][0] = 3;
a[1][1] = 4;
for (int i = 2; i < n; i++) {
for (int j = 0; j <= i; j++) {
a[i][j] = (i + 1) * 2 - 1;
if (j == i) {
a[i][j] = (i + 1) * 2;
}
}
for (int j = 0; j < i; j++) {
a[j][i] = (i + 1) * 2 - 1;
}
}
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
pw.print(a[i][j] + " ");
}
pw.println();
}
}
pw.flush();
}

浙公网安备 33010602011771号