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();
}