Neowise Labs Contest 1 (Codeforces Round 1018, Div. 1 + Div. 2)


A. Wonderful Sticks

题意:给你一个字符串,构造一个排列,满足如果\(s_i =\)'>'则\(p_{i + 1}\)大于前面所有数,否则小于前面所有数。

从后往前构造,遇到小于号放当前最小的数,否则放最大的数。

点击查看代码
void solve() {
    int n;
    std::cin >> n;
    std::string s;
    std::cin >> s;
    std::vector<int> a(n);
    int l = 0, r = n - 1;
    for (int i = n - 2; i >= 0; -- i) {
    	if (s[i] == '>') {
    		a[i + 1] = r -- ;
    	} else {
    		a[i + 1] = l ++ ;
    	}
    }

    a[0] = l;
    for (int i = 0; i < n; ++ i) {
    	std::cout << a[i] + 1 << " \n"[i == n - 1];
    }
}

B. Wonderful Gloves

题意:\(n\)种手套,每种手套分左右,分别有\(l_i, r_i\)个。如果一种手套左右分别被拿了一个,就凑齐了着一类手套。问凑起\(k\)对手套需要拿几次。

考虑最坏的情况,从每对手套里都完整的一类拿出来,也就是\(\sum_{i=1}^{n} \max(l_i, r_i)\)。这样我们一对手套还没凑齐,然后剩下的数里选前\(k-1\)大的拿走,然后答案加一就行。

点击查看代码
void solve() {
    int n, k;
    std::cin >> n >> k;
    std::vector<int> l(n), r(n);
    for (int i = 0; i < n; ++ i) {
    	std::cin >> l[i];
    }

    for (int i = 0; i < n; ++ i) {
    	std::cin >> r[i];
    }

    std::vector<int> a;
    i64 ans = 0;
    for (int i = 0; i < n; ++ i) {
    	if (l[i] < r[i]) {
    		std::swap(l[i], r[i]);
    	}

		ans += l[i];
		a.push_back(r[i]);
    }

    std::ranges::sort(a, std::greater<>());
    for (int i = 0; i + 1 < k; ++ i) {
    	ans += a[i];
    }

    std::cout << ans + 1 << "\n";
}

C. Wonderful City

题意:一个矩阵,你可以对每行每列最多操作一次,将这一行这一列全部数加一,代价分别为\(a_i, b_j\)。问没有两个相邻数相等的最小代价。

发现行列可以分开讨论,因为操作行不会影响这一行相邻两个数的差,操作列不会影响这一列相邻两个数的差。那么做两次\(dp\)。以行为例:\(f[i][0/1]\)表示第\(i\)行有没有操作的最小代价。那么枚举下一行有没有操作,然后加上操作值后判断这两行是不是合法的,也就是没有相邻两个数相等。如果合法就可以转移\(f[i + 1][y] = \min(f[i + 1][y] + [y == 1]a_{i+1}, f[i][x])\)
列同理,最后两个加起来就行。

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

    std::vector<i64> b(n);
    for (int i = 0; i < n; ++ i) {
        std::cin >> b[i];
    }

    const i64 inf = 1e18;
    std::array<i64, 2> f{inf, inf};
    f[0] = 0; f[1] = a[0];
    for (int i = 0; i + 1 < n; ++ i) {
	    std::array<i64, 2> g{inf, inf};
    	for (int x = 0; x < 2; ++ x) {
    		for (int y = 0; y < 2; ++ y) {
    			bool flag = true;
    			for (int j = 0; j < n; ++ j) {
    				if (h[i][j] + x == h[i + 1][j] + y) {
    					flag = false;
    					break;
    				}
    			}

    			if (flag) {
    				g[y] = std::min(g[y], f[x] + y * a[i + 1]);
    			}
    		}
    	}

    	f = g;
    }

    i64 ans = std::min(f[0], f[1]);
    if (ans == inf) {
    	std::cout << -1 << "\n";
    	return;
    }

    f[0] = 0; f[1] = b[0];
    for (int j = 0; j + 1 < n; ++ j) {
	    std::array<i64, 2> g{inf, inf};
    	for (int x = 0; x < 2; ++ x) {
    		for (int y = 0; y < 2; ++ y) {
    			bool flag = true;
    			for (int i = 0; i < n; ++ i) {
    				if (h[i][j] + x == h[i][j + 1] + y) {
    					flag = false;
    					break;
    				}
    			}

    			if (flag) {
    				g[y] = std::min(g[y], f[x] + y * b[j + 1]);
    			}
    		}
    	}

    	f = g;
    }

    i64 ans1 = std::min(f[0], f[1]);
    if (ans1 == inf) {
    	std::cout << -1 << "\n";
    	return;
    }

    std::cout << ans + ans1 << "\n";
}

D. Wonderful Lightbulbs

题意:一开始有一个位置是亮的,随后进行了若干次操作,每次操作选择一个\((x, y)\),然后更改\((x, y), (x + 1, y), (x, y + 1), (x, + 1, y - 1)\)的状态。选择给出操作后亮着的灯,求一开始的位置。

亮着的灯一定是奇数,因为每次改变四个灯,而一开始亮着的灯是\(1\)个,也就是奇数个,每次改变偶数个依然是奇数。
然后假设一开始是\((x, y)\)亮着,那么如果\(x' = x\)的位置一定是奇数个亮着,因为这一列每次操作了\((x, y), (x, y + 1)\)两个。同理对角线\(x' + y' = x + y\)上的灯也是奇数个。那么我们只需要记录\(x\)坐标亮着灯的个数,和每个对角线灯的个数。找两个奇数个的,它们的交点就是答案。

点击查看代码
void solve() {
    int n;
    std::cin >> n;
    std::vector<std::pair<int, int>> a(n);
    for (int i = 0; i < n; ++ i) {
    	std::cin >> a[i].first >> a[i].second;
    }

    std::map<int, int> X, diag;
    for (auto & [x, y] : a) {
    	++ X[x];
    	++ diag[x + y];
    }

    int x = 0, y = 0;
    for (auto & [z, cnt] : X) {
    	if (cnt & 1) {
    		x = z;
    		break;
    	}
    }

    for (auto & [z, cnt] : diag) {
    	if (cnt & 1) {
    		y = z - x;
    		break;
    	}
    }

    std::cout << x << " " << y << "\n";
}

E. Wonderful Teddy Bears

题意:一个只有\(PB\)的字符串,要把\(B\)都移到左边,\(P\)都移到右边。每次给一个长度为\(3\)的子串排序。求最少操作次数。

可以往逆序对的方向想。那么我们可以操作\(PBB, PPB\)这两个,每次使得逆序对减少\(2\)。那么至少需要逆序对数除\(2\)的操作数。这是理想情况,但实际可能有\(PBPBPBP..\)这样交错的子串。
设有\(cnt1\)\(B\),有\(cnt2\)个在偶数位置上,因为排序后一定是\(\lfloor \frac{cnt1}{2} \rfloor\)\(B\)在偶数位置上,同时发现操作\(PBB, PPB\)不会改变\(cnt2\)的个数。于是我们只能借助\(PBP, BPB\)来改变\(cnt2\)的个数。那么需要\(\lfloor \frac{cnt1}{2} \rfloor - cnt2\)次操作,这样序列就会变成我们想要的样子,就可以按开始的操作方式来做了。同时发现操作\(PBP, BPB\)会使得逆序对减少\(1\),那么记一开始逆序对为\(cnt\)个。那么剩下\(cnt - (\lfloor \frac{cnt1}{2} \rfloor - cnt2)\)个逆序对,这一部分操作数为\(\frac{cnt - (\lfloor \frac{cnt1}{2} \rfloor - cnt2)}{2}\),另一部分操作数为\(\lfloor \frac{cnt1}{2} \rfloor - cnt2\),加起来就是\(\frac{cnt + (\lfloor \frac{cnt1}{2} \rfloor - cnt2)}{2}\)

点击查看代码
void solve() {
    int n;
    std::cin >> n;
    std::string s;
    std::cin >> s;
    i64 ans = 0, cnt1 = 0, cnt2 = 0;
    for (int i = n - 1; i >= 0; -- i) {
    	if (s[i] == 'B') {
    		++ cnt1;
    		if (i & 1) {
    			++ cnt2;
    		}
    	} else {
    		ans += cnt1;
    	}
    }

   	ans = (ans + std::abs(cnt1 / 2 - cnt2));
   	std::cout << ans / 2 << "\n";
}
posted @ 2025-04-20 15:14  maburb  阅读(271)  评论(0)    收藏  举报