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\) 条边,然后合并相同颜色的所有点并标记。

对于不同颜色的点相连,直接对两个连通块操操作:

  1. 如果两个连通块内部还没有自行相连,那么应该加上 \(size_u - 1 + size_v\) 条边;
  2. 如果两个连通块其中一个自行相连,另一个没有相连,那么应该加上 \(size_{没有合并的连通块}\) 条边;
  3. 如果两个连通块都已经自行相连,那么加上 \(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();
    }

posted @ 2025-09-06 08:08  dbywsc  阅读(16)  评论(0)    收藏  举报