Codeforces Round 981 (Div. 3)ABCDE
A. Sakurako and Kosuke
藕是看样例直接猜了结论😜
// AC one more times
// nndbk
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
const int N = 2e5 + 10;
int main()
{
ios::sync_with_stdio(false); cin.tie(nullptr), cout.tie(nullptr);
//-1
//3
//5
//7
int t; cin>>t;
while(t--)
{
ll x; cin>>x;
cout<<(x%2!=0?"Kosuke":"Sakurako")<<"\n";
}
return 0;
}
B. Sakurako and Water
思路:按从是负数的里面小到大遍历这条最长的主对角线,贡献就是加上这个最小的。
// AC one more times
// nndbk
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
const int N = 2e3 + 10;
int a[N][N];
array<int,3>b[250010];
bool cmp(array<int,3> a,array<int,3> b)
{
return a[0] < b[0];
}
int main()
{
ios::sync_with_stdio(false); cin.tie(nullptr), cout.tie(nullptr);
int t; cin>>t;
while(t--)
{
int n; cin>>n;
int cnt = 0;
for(int i = 1;i <= n; i++)
{
for(int j = 1;j <= n; j++)
{
cin>>a[i][j];
if(a[i][j] < 0)
{
b[++cnt] = {a[i][j],i,j};
}
}
}
sort(b+1,b+1+cnt,cmp);
ll ans = 0;
for(int i = 1;i <= cnt; i++)
{
auto [v,x,y] = b[i];
if(a[x][y] >= 0)continue;
ans += (-a[x][y]);
int tx = x-1,ty = y-1;
while(tx >= 1 && ty >= 1)
{
a[tx][ty] += (-a[x][y]);
tx--,ty--;
}
tx = x+1,ty = y+1;
while(tx <= n && ty <= n)
{
a[tx][ty] += (-a[x][y]);
tx++,ty++;
}
a[x][y] = 0;
}
cout<<ans<<"\n";
}
return 0;
}
C. Sakurako's Field Trip
思路:因为\(a_i\)只能和\(a_{n-i+1}\)交换,相当于我们只用看一半就好了。分别看交换和不交换的贡献,如果交换后小那么就交换。最后看一样的数的个数。
// AC one more times
// nndbk
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
const int N = 2e5 + 10;
int a[N];
int n;
int main()
{
ios::sync_with_stdio(false); cin.tie(nullptr), cout.tie(nullptr);
int t; cin>>t;
while(t--)
{
cin>>n;
for(int i = 1;i <= n; i++)
cin>>a[i];
int ans = 0;
int n1 = 0,n2 = 0;
for(int i = 2;i <= n/2; i++)
{
n1 = 0,n2 = 0;
//a[i-1] a[i] ... a[n-i+1] a[n-i+2]
if(a[i] == a[i-1])
n1++;
if(a[n-i+1] == a[n-i+2])//n-(i-1)+1 = n-i+2
n1++;
if(a[i] == a[n-i+1])
n2++;
if(a[i-1] == a[n-i+2])
n2++;
if(n2 < n1)
swap(a[i],a[n-i+1]);
}
for(int i = 1;i < n; i++)
if(a[i] == a[i+1])
ans++;
cout<<ans<<'\n';
}
return 0;
}
D. Kousuke's Assignment
思路:很容易想到前缀和,做完前缀和之后,用last记录上一次成为答案的最右边的坐标。注意0的情况直接取本身就好了。mp[i]表示i上一次出现的位置,如果mp[i]>=last说明不会重叠可以成为答案,更新last = i。因为0是一定可以的,mp[0]初始化为-1就好了。
// AC one more times
// nndbk
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
const int N = 2e5 + 10;
ll a[N],s[N];
int main()
{
ios::sync_with_stdio(false); cin.tie(nullptr), cout.tie(nullptr);
int t; cin>>t;
while(t--)
{
int n; cin>>n;
for(int i = 1;i <= n; i++)
cin>>a[i];
for(int i = 1;i <= n; i++)
s[i] = s[i-1]+a[i];
// for(int i = 1;i <= n; i++)
// cout<<s[i]<<" ";
map<ll,int>mp;
int cnt = 0,last = -1;
mp[0] = -1;
for(int i = 1;i <= n; i++)
{
if(mp.count(s[i]))
{
if(mp[s[i]] >= last)
{
last = i;
cnt++;
}
}
mp[s[i]] = i;
}
cout<<cnt<<"\n";
}
return 0;
}
E. Sakurako, Kosuke, and the Permutation
题意:给你一个排列(\(1~n\)),这个排列被认为是简单的需要满足下面其中一个条件:
对于每一个\(i(1\le i\le n)\)
- \(p_i = i\)
- \(p_{p_i} = i\)
问你最少交换多少次使得这个排列是简单的。
思路:置换环(求解数组排序元素间所需最小交换次数这类问题)
思想:置换环将每个元素指向其排序后应在的位置,最终首位相连形成一个环(若数字在最终位置,则其自身成环)
对于\(i\rightarrow p[i] \rightarrow p[p[i]] \rightarrow p[p[p[i]]] \rightarrow ... \rightarrow i\) 这样子的会构成一个环。需要满足条件的话,环的长度需要\(\ge 2\)。即,最后这个数列转化为图一定是若干个自环和若干个两数环所组成的图。
对于环长度\(\le 2\)的,要进行交换操作让环长度变短。我们发现让\(p[p[i]] = i\)比让\(p[i] = i\)更优,为什么呢?
我们设\(mp[i]\)为数值为\(i\)的下标。以\(2,3,4,1\)为例:
\(mp[1] = 4\),\(p[1] = 2\),我们想让\(p[p[1]] = 1\)即\(p[2] = 1\),那么\(p[mp[1]] = p[2]\),即\(p[4] = 3\)
交换完一次之后变成了\(2,1,4,3\)。同时更新\(mp[1] = 2,mp[3] = 4\)
这样必定可以一次修改两个数,是优于\(p[i]\)改成\(i\)的,那么对于环长度为\(size\)的,交换次数为\(\lfloor\dfrac{size-1}{2} \rfloor\)。
#include <bits/stdc++.h>
using namespace std;
struct DSU {
std::vector<int> f, siz;
DSU() {}
DSU(int n) {
init(n);
}
void init(int n) {
f.resize(n + 1);
std::iota(f.begin(), f.end(), 0);
siz.assign(n + 1, 1);
}
int find(int x) {
while (x != f[x]) {
x = f[x] = f[f[x]];
}
return x;
}
bool same(int x, int y) {
return find(x) == find(y);
}
bool merge(int x, int y) {
x = find(x);
y = find(y);
if (x == y) {
return false;
}
siz[x] += siz[y];
f[y] = x;
return true;
}
int size(int x) {
return siz[find(x)];
}
};
int main()
{
int t; cin>>t;
while(t--)
{
int n; cin>>n;
DSU d(n);
for(int i = 1;i <= n; i++)
{
int x; cin>>x;
d.merge(i,x);
}
set<int>s;
for(int i = 1;i <= n; i++){
d.f[i] = d.find(i);
s.insert(d.f[i]);
}
int ans = 0;
for(auto x : s)
{
ans += (d.siz[x]-1)/2;
}
cout<<ans<<"\n";
}
return 0;
}
或者直接模拟上述过程也是可以的:
// AC one more times
// nndbk
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
const int N = 1e5 + 10;
int main()
{
ios::sync_with_stdio(false); cin.tie(nullptr), cout.tie(nullptr);
int t; cin>>t;
while(t--)
{
int n; cin>>n;
map<int,int>mp;
vector<int>p(n+1);
for(int i = 1;i <= n; i++){
cin>>p[i];
mp[p[i]] = i;
}
int ans = 0;
for(int i = 1;i <= n; i++)
{
if(p[i] == i || p[p[i]] == i)
continue;
int s = mp[i],t = p[i];
swap(p[s],p[t]);
mp[p[s]] = s;
mp[p[t]] = t;
ans++;
}
cout<<ans<<"\n";
}
return 0;
}
//2 3 4 1
//2 1 4 3
//2 3 4 5 1
//2 1 4 5 3
//2 1 4 3 5