Codeforces Round 957 (Div.2)(A-D題解)

_yimg發表於2024-07-10

Codeforces Round 957 (Div. 3)

A

簽到題

程式碼:

#include<bits/stdc++.h>
using namespace std;
void work()
{
	int n;
	cin >> n;
	for(int i = 1; i <= n; ++i)
		cout << i << " ";
	cout << '\n';
}
int main()
{
	int t;
	cin >> t;
	while(t--)
		work();	
}

B

題意:

給兩個矩陣A, B, 可以選任意長寬大於等於2的子矩陣,令其中一對對角元素+1mod3, 一對+2mod3,問能否用A得到B

思路:

首先可以把+xmod3的操作理解為三進位制下按位異或,操作矩陣為P, $ A xor P_1 xor \cdots xorP_n=B$

兩邊左乘兩個A, 得 \(P_1 xor P_2xor\cdots xorP_n = AxorAxorB\)

接下來只需要驗證 XOR P 為合法操作序列就ok了, 因為我們選用的操作矩陣是可逆的(\(1xor2=0\)),所以可以模擬操作過程,用同樣操作令操作矩陣化為0則合法。

更優的做法是,我們發現操作矩陣的行異或和 and 列異或和為0,再考慮此命題的必要性成立,所以可以透過此方法驗證操作矩陣,或者直接透過A,B的行、列異或和來判斷是非存在這麼一個操作矩陣

程式碼:

#include<bits/stdc++.h>
using namespace std;
void work()
{
	int n, m;
	cin >> n >> m;
	vector<vector<int>> c(n + 1, vector<int>(m + 1));
	for(int i = 1; i <= n; ++i)
		for(int j = 1; j <= m; ++j){
			char d; cin >> d;
			c[i][j] = (d - '0')*2%3;
		}
	for(int i = 1; i <= n; ++i)
		for(int j = 1; j <= m; ++j){
			char d; cin >> d;
			c[i][j] += d - '0';
			c[i][j] %= 3;
		}
	vector<int> r(n + 1), col(m + 1);
	for(int i = 1; i <= n; ++i){
		for(int j = 1; j <= m; ++j){
			r[i] += c[i][j]; r[i] %= 3;
			col[j] += c[i][j]; col[j] %= 3;
		}
	}
	bool ans = 0;
	for(int i = 1; i <= n; ++i) ans |= r[i];
	for(int i = 1; i <= m; ++i) ans |= col[i];
	cout << (!ans ? "YES" : "NO") << '\n';
}
int main()
{
	int t;
	cin >> t;
	while(t--)
		work();	
}

C

題意:

三個人分n塊蛋糕,每個人對每塊蛋糕都有一個估值,三人對所有蛋糕總價值的估值相同為tot,問能否給出三個不相交的區間使得\(\sum_{i = l_a}^{r_a}a_i ,\sum_{i = l_b}^{r_b},\sum_{i=l_c}^{r_c} \ge \lceil tot/3 \rceil\) 成立。

思路:

如果給定了三個人的選取順序,直接貪心選取,選夠了換下一個人,看能否使得三個人都滿意。所以可以列舉\(3!\) 種情況。

程式碼:

#include<bits/stdc++.h>
using namespace std;
using ll = long long;
void work()
{
	int n;
	cin >> n;
	vector<vector<int>> a(4, vector<int>(n + 1));
	for(int i = 1; i <= 3; ++i)
		for(int j = 1; j <= n; ++j)
			cin >> a[i][j];
	ll tot = 0;
	for(int i = 1; i <= n; ++i) tot += a[1][i];
	vector<int> perm({0, 1, 2, 3});
	do{
		int l = 1, pt = 1;
		vector<int> ans(4);
		ll sum = 0;
		while(l <= n && pt <= 3){
			sum += a[perm[pt]][l++];
			if(sum >= (tot + 2) / 3){
				sum = 0;
				ans[pt++] = l;
			}
		}
		if(pt > 3){
			vector<pair<int, int>> b(4);
			b[perm[1]] = {1, ans[1] - 1};
			b[perm[2]] = {ans[1], ans[2] - 1};
			b[perm[3]] = {ans[2], ans[3] - 1};
			for(int i = 1; i <= 3; ++i){
				cout << b[i].first << " " << b[i].second << " ";
			}
			cout << '\n';
			return;
		}
	}while(next_permutation(perm.begin(), perm.end()));
	cout << "-1\n";
}
int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	int t;
	cin >> t;
	while(t--)
		work();	
}

D

題意:

給定兩個長度為n的陣列A, B(兩陣列中均無重複元素), 問能否透過任意次\(\forall i-j=l-r, swap(a_i, a_j),swap(b_l,b_r)\) 能否使A等於B

思路:

沒有限制我們交換的次數所以可以考慮全用相鄰兩位交換,發現能透過一般交換操作實現的,一定可以透過相鄰兩位交換實現,固定其中一個陣列讓其固定的相鄰兩位交換偶數次,陣列都是不變的,所以只用考慮交換次數奇偶就能判斷是否可以使A等於B,偶數YES,奇數NO。對B陣列離散,交換次數等於逆序對數,樹狀陣列求解

程式碼:

#include<bits/stdc++.h>
using namespace std;
using ll = long long;
void work()
{
	int n;
	cin >> n;
	vector<int> a(n + 1), b(n + 1);
	map<int, int> mp;
	for(int i = 1; i <= n; ++i){
		cin >> a[i];
		mp[a[i]] = i;
	}
	bool ok = 1;
	for(int i = 1; i <= n; ++i){
		cin >> b[i]; if(!mp[b[i]]) ok = 0;
		b[i] = mp[b[i]];
	}
	if(!ok){
		cout << "NO\n";
		return;
	}
	vector<int> tr(n<<1|1);
	auto add = [&](int p){
		for(;p <= n; p += p&-p){
			tr[p]++;
		}
	};
	auto ask = [&](int p){
		int res = 0;
		for(;p; p -= p&-p){
			res += tr[p];
		}
		return res;
	};
	int ans = 0;
	for(int i = 1; i <= n; ++i){
		add(b[i]);
		ans += i - ask(b[i]);
	}
	if(ans&1) cout << "NO\n";
	else cout << "YES\n";
}
int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	int t;
	cin >> t;
	while(t--)
		work();	
}

相關文章