Codeforces Round 789 div2 A-E题解 & 处理手法思考
A. Tokitsukaze and All Zero Sequence
这题给一个数列,每次操作
- 对于两个不相同的数字可以吧大的变成min,
- 两个相同的话一个变为0
问最少操作多少次能将整个数组变为0
首先这个操作1开始容易想到,如果数组中原来有0,那么就可以操作n - |0的个数|次使得数组全部变为0
然后我们再看操作2,显然我们可以先把一个数字变成0,然后再用操作2去同化其他非0的,那么如果存在两个相同的数字,我们可以省略步骤1,使用步骤2进行操作
如果不存在的话,那么还需要一步把另一个数字变成相同的数字才能变0,这个很好想
#include <bits/stdc++.h>
using namespace std;
constexpr int limit = (2000000 + 5);//防止溢出
#define INF 0x3f3f3f3f
#define inf 0x3f3f3f3f3f
#define lowbit(i) i&(-i)//一步两步
#define EPS 1e-9
#define FASTIO ios::sync_with_stdio(false);cin.tie(0),cout.tie(0);
#define ff(a) printf("%d\n",a );
#define pi(a, b) pair<a,b>
#define rep(i, a, b) for(ll i = a; i <= b ; ++i)
#define per(i, a, b) for(ll i = b ; i >= a ; --i)
#define MOD 998244353
#define traverse(u) for(int i = head[u]; ~i ; i = edge[i].nxt)
#define FOPEN freopen("C:\\Users\\tiany\\CLionProjects\\akioi\\data.txt", "rt", stdin)
#define FOUT freopen("C:\\Users\\tiany\\CLionProjects\\akioi\\dabiao.txt", "wt", stdout)
typedef long long ll;
typedef unsigned long long ull;
char buf[1 << 23], *p1 = buf, *p2 = buf, obuf[1 << 23], *O = obuf;
inline ll read() {
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
ll sign = 1, x = 0;
char s = getchar();
while (s > '9' || s < '0') {
if (s == '-')sign = -1;
s = getchar();
}
while (s >= '0' && s <= '9') {
x = (x << 3) + (x << 1) + s - '0';
s = getchar();
}
return x * sign;
#undef getchar
}//快读
void print(ll x) {
if (x / 10) print(x / 10);
*O++ = x % 10 + '0';
}
void write(ll x, char c = 't') {
if (x < 0)putchar('-'), x = -x;
print(x);
if (!isalpha(c))*O++ = c;
fwrite(obuf, O - obuf, 1, stdout);
O = obuf;
}
constexpr ll mod = 1e9 + 7;
ll quick_pow(ll base, ll expo){
ll ans = 1;
while(expo){
if(expo & 1) ans = ans * base % mod;
base = base * base % mod;
expo >>= 1;
}
return ans;
}
int n,m;
int a[limit];
void solve(){
cin>>n;
map<int, int>mp;
rep(i, 1, n){
cin>>a[i];
mp[a[i]]++;
}
//如果是0的话就直接用0输出
if(mp[0]){
cout<<n - mp[0]<<endl;
return;
}else{
int ans = 2;
for(auto [k, v] : mp){
if(v > 1){
ans--;
break;
}
}
cout<<ans + n - 1<<endl;
}
};
int32_t main() {
#ifdef LOCAL
FOPEN;
// FOUT;
#endif
FASTIO
int kase;
cin>>kase;
while (kase--)
solve();
cerr << "Time elapsed: " << 1.0 * clock() / CLOCKS_PER_SEC << "s\n";
return 0;
}
B1. Tokitsukaze and Good 01-String (easy version)
B题问分若干段,每段长度为偶数,那么问最少需要改动多少个位置才能完成这种划分
这题问的方式很有误导性,我直接写了个模拟,但捂在手里不敢交,后来想了想好像没问题,交了一发AC
#include <bits/stdc++.h>
using namespace std;
constexpr int limit = (2000000 + 5);//防止溢出
#define INF 0x3f3f3f3f
#define inf 0x3f3f3f3f3f
#define lowbit(i) i&(-i)//一步两步
#define EPS 1e-9
#define FASTIO ios::sync_with_stdio(false);cin.tie(0),cout.tie(0);
#define ff(a) printf("%d\n",a );
#define pi(a, b) pair<a,b>
#define rep(i, a, b) for(ll i = a; i <= b ; ++i)
#define per(i, a, b) for(ll i = b ; i >= a ; --i)
#define MOD 998244353
#define traverse(u) for(int i = head[u]; ~i ; i = edge[i].nxt)
#define FOPEN freopen("C:\\Users\\tiany\\CLionProjects\\akioi\\data.txt", "rt", stdin)
#define FOUT freopen("C:\\Users\\tiany\\CLionProjects\\akioi\\dabiao.txt", "wt", stdout)
typedef long long ll;
typedef unsigned long long ull;
char buf[1 << 23], *p1 = buf, *p2 = buf, obuf[1 << 23], *O = obuf;
inline ll read() {
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
ll sign = 1, x = 0;
char s = getchar();
while (s > '9' || s < '0') {
if (s == '-')sign = -1;
s = getchar();
}
while (s >= '0' && s <= '9') {
x = (x << 3) + (x << 1) + s - '0';
s = getchar();
}
return x * sign;
#undef getchar
}//快读
void print(ll x) {
if (x / 10) print(x / 10);
*O++ = x % 10 + '0';
}
void write(ll x, char c = 't') {
if (x < 0)putchar('-'), x = -x;
print(x);
if (!isalpha(c))*O++ = c;
fwrite(obuf, O - obuf, 1, stdout);
O = obuf;
}
constexpr ll mod = 1e9 + 7;
ll quick_pow(ll base, ll expo){
ll ans = 1;
while(expo){
if(expo & 1) ans = ans * base % mod;
base = base * base % mod;
expo >>= 1;
}
return ans;
}
int n,m;
int a[limit];
void solve(){
cin>>n;
string str;
cin>>str;
str = " " + str;
vector<pi(int, int)>v;
rep(i,1,n){
if(v.empty() || v.back().first != str[i] - '0'){
v.push_back({str[i] - '0', 1});
}else{
v.back().second++;
}
}
int ans = 0;
for(int i = 0; i < (int)v.size() - 1; i++){
if(v[i].second & 1){
ans++;
v[i + 1].second--;
}
}
cout<<ans<<endl;
};
int32_t main() {
#ifdef LOCAL
FOPEN;
// FOUT;
#endif
FASTIO
int kase;
cin>>kase;
while (kase--)
solve();
cerr << "Time elapsed: " << 1.0 * clock() / CLOCKS_PER_SEC << "s\n";
return 0;
}
B2. Tokitsukaze and Good 01-String (hard version)
这个题和上个题题意相同,但多问了个,如果分段要求段数最少,该怎么改
首先这种问题不要懒,很多easy和hard version其实是两种不同的问题解决方法,不要蹲在easy上去想hard。上次div3教训应该是很深刻了。
这个我们需要回归本质,去想偶数,那么所有偶数都能被2整除,2就是偶数最小单位,我们去看这个最小单位,然后如果我们有n/2个这种单位,那么每个单位全0或者全1的费用可以很容易地被算出来,然后上一段的颜色是什么就不会影响这一段的了,我们可以定义状态了,dp[i][j], i代表位数,j代表以i结尾的2段全0还是全1
然后我们只需要从上一个继承过来最小费用就行了
#include <bits/stdc++.h>
using namespace std;
constexpr int limit = (2000000 + 5);//防止溢出
#define INF 0x3f3f3f3f
#define inf 0x3f3f3f3f3f
#define lowbit(i) i&(-i)//一步两步
#define EPS 1e-9
#define FASTIO ios::sync_with_stdio(false);cin.tie(0),cout.tie(0);
#define ff(a) printf("%d\n",a );
#define pi(a, b) pair<a,b>
#define rep(i, a, b) for(ll i = a; i <= b ; ++i)
#define per(i, a, b) for(ll i = b ; i >= a ; --i)
#define MOD 998244353
#define traverse(u) for(int i = head[u]; ~i ; i = edge[i].nxt)
#define FOPEN freopen("C:\\Users\\tiany\\CLionProjects\\akioi\\data.txt", "rt", stdin)
#define FOUT freopen("C:\\Users\\tiany\\CLionProjects\\akioi\\dabiao.txt", "wt", stdout)
typedef long long ll;
typedef unsigned long long ull;
char buf[1 << 23], *p1 = buf, *p2 = buf, obuf[1 << 23], *O = obuf;
inline ll read() {
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
ll sign = 1, x = 0;
char s = getchar();
while (s > '9' || s < '0') {
if (s == '-')sign = -1;
s = getchar();
}
while (s >= '0' && s <= '9') {
x = (x << 3) + (x << 1) + s - '0';
s = getchar();
}
return x * sign;
#undef getchar
}//快读
void print(ll x) {
if (x / 10) print(x / 10);
*O++ = x % 10 + '0';
}
void write(ll x, char c = 't') {
if (x < 0)putchar('-'), x = -x;
print(x);
if (!isalpha(c))*O++ = c;
fwrite(obuf, O - obuf, 1, stdout);
O = obuf;
}
constexpr ll mod = 1e9 + 7;
ll quick_pow(ll base, ll expo){
ll ans = 1;
while(expo){
if(expo & 1) ans = ans * base % mod;
base = base * base % mod;
expo >>= 1;
}
return ans;
}
int n,m;
int a[limit];
int dp[limit][2];
void solve(){
cin>>n;
string str;
cin>>str;
str = " " + str;
vector<pi(int, int)>v;
rep(i,1,n){
if(v.empty() or v.back().first != str[i] - '0'){
v.push_back({str[i] - '0', 1});
}else{
v.back().second++;
}
}
int ans = 0;
for(int i = 0; i < (int)v.size() - 1; i++){
if(v[i].second & 1){
ans++;
v[i + 1].second--;
v[i].second++;
}
}
if(!ans){
cout<<ans<<" "<<v.size()<<endl;
return;
}
rep(i,1,n){
fill(dp[i], dp[i] + 2, INF);
}
if(str[1] != str[2]){
dp[1][0] = 1;
dp[1][1] = 1;
}else{
dp[1][str[1] - '0'] = 1;
}
for(int i = 3; i <= n; i += 2){
if(str[i] != str[i + 1]){
dp[i][0] = min(dp[i - 2][0], dp[i - 2][1] + 1);
dp[i][1] = min(dp[i - 2][1], dp[i - 2][0] + 1);
}else{
int x = str[i] - '0';
dp[i][x] = min(dp[i - 2][x], dp[i - 2][x ^ 1] + 1);
}
}
cout<<ans<<" "<<*min_element(dp[n - 1], dp[n - 1] + 2)<<endl;
};
int32_t main() {
#ifdef LOCAL
FOPEN;
// FOUT;
#endif
FASTIO
int kase;
cin>>kase;
while (kase--)
solve();
cerr << "Time elapsed: " << 1.0 * clock() / CLOCKS_PER_SEC << "s\n";
return 0;
}
其实也没有很难
C. Tokitsukaze and Strange Inequality
这题是挑选下标四元组(a,b,c,d),要求abcd为升序,不能重合,并且要满足第一个下标元素小于第三个下标,第二个下标元素小于第四个
输入distinct
那么这个问题很容易想到用树状数组去维护,输入5000,应该是平方算法,所以我们可以枚举两个支点,观察发现最好维护的地方就是枚举b和c,那么我们只要找出来
- b前面小于d的
- c后面小于a的
这两个都很容易能够求到
乘起来就行了
#include <bits/stdc++.h>
using namespace std;
constexpr int limit = (2000000 + 5);//防止溢出
#define INF 0x3f3f3f3f
#define inf 0x3f3f3f3f3f
#define lowbit(i) i&(-i)//一步两步
#define EPS 1e-9
#define FASTIO ios::sync_with_stdio(false);cin.tie(0),cout.tie(0);
#define ff(a) printf("%d\n",a );
#define pi(a, b) pair<a,b>
#define rep(i, a, b) for(ll i = a; i <= b ; ++i)
#define per(i, a, b) for(ll i = b ; i >= a ; --i)
#define MOD 998244353
#define traverse(u) for(int i = head[u]; ~i ; i = edge[i].nxt)
#define FOPEN freopen("C:\\Users\\tiany\\CLionProjects\\akioi\\data.txt", "rt", stdin)
#define FOUT freopen("C:\\Users\\tiany\\CLionProjects\\akioi\\dabiao.txt", "wt", stdout)
typedef long long ll;
typedef unsigned long long ull;
char buf[1 << 23], *p1 = buf, *p2 = buf, obuf[1 << 23], *O = obuf;
inline ll read() {
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
ll sign = 1, x = 0;
char s = getchar();
while (s > '9' || s < '0') {
if (s == '-')sign = -1;
s = getchar();
}
while (s >= '0' && s <= '9') {
x = (x << 3) + (x << 1) + s - '0';
s = getchar();
}
return x * sign;
#undef getchar
}//快读
void print(ll x) {
if (x / 10) print(x / 10);
*O++ = x % 10 + '0';
}
void write(ll x, char c = 't') {
if (x < 0)putchar('-'), x = -x;
print(x);
if (!isalpha(c))*O++ = c;
fwrite(obuf, O - obuf, 1, stdout);
O = obuf;
}
constexpr ll mod = 1e9 + 7;
ll quick_pow(ll base, ll expo){
ll ans = 1;
while(expo){
if(expo & 1) ans = ans * base % mod;
base = base * base % mod;
expo >>= 1;
}
return ans;
}
int n,m;
int a[limit];
int tree[limit];
#define int ll
int tree2[limit];
void add(int pos, int val){
while(pos <= n){
tree[pos] += val;
pos += lowbit(pos);
}
}
int query(int pos){
int ans = 0;
while(pos){
ans += tree[pos];
pos -= lowbit(pos);
}
return ans;
}
void add2(int pos, int val){
while(pos <= n){
tree2[pos] += val;
pos += lowbit(pos);
}
}
int query2(int pos){
int ans = 0;
while(pos){
ans += tree2[pos];
pos -= lowbit(pos);
}
return ans;
}
void solve(){
cin>>n;
rep(i,1,n){
cin>>a[i];
}
ll ans = 0;
rep(i,1,n){
rep(j,1,n){
tree[j] = 0;
tree2[j] = 0;
}
rep(j,1, i - 1){
add(a[j], 1);
}
//把后面的清空
per(j,i + 1,n){
add2(a[j], 1);
}
rep(j, i + 1, n){
add2(a[j], -1);
ll fst = query(a[j]);
ll scd = query2(a[i] - 1);
ans += fst * scd;
}
}
cout<<ans<<endl;
};
int32_t main() {
#ifdef LOCAL
FOPEN;
// FOUT;
#endif
FASTIO
int kase;
cin>>kase;
while (kase--)
solve();
cerr << "Time elapsed: " << 1.0 * clock() / CLOCKS_PER_SEC << "s\n";
return 0;
}
D. Tokitsukaze and Meeting
太麻烦没看,不会,直接跳E
E. Tokitsukaze and Two Colorful Tapes
这个题是给出两个数组permutation,如果是相同元素位置要填一样的数字,问 \sum{abs(a[i] - b[i])}的最大值
读题花了半个小时
这个问题经过上次gdb 2022的时候的懵逼,看到这种问题首先想到找环,然后想到了带权并查集,我说上次怎么这么多人直接想到环了,搜得死奶,学到了。
感觉这类问题的处理手法往往非常类似,比如两个数组中a和b必须存在某种constraint,那么这种约束肯定有某种传递性,因为是两个数组,所以要么是孤点,要么是简单环,然后根据环我们再想其他的。其实他喵的这种问题就很妙,但是你题量不够还是不行。
然后找环之后,根据环大小排个序,我们贪心的交错安排权值,记得最后要减一下。
然而wa2,然后看题解发现,好像点个数为奇数的环并不能对答案产生什么有效贡献,所以奇数的那个点我们不主动安排权值,避免影响后面的
#include <bits/stdc++.h>
using namespace std;
constexpr int limit = (2000000 + 5);//防止溢出
#define INF 0x3f3f3f3f
#define inf 0x3f3f3f3f3f
#define lowbit(i) i&(-i)//一步两步
#define EPS 1e-9
#define FASTIO ios::sync_with_stdio(false);cin.tie(0),cout.tie(0);
#define ff(a) printf("%d\n",a );
#define pi(a, b) pair<a,b>
#define rep(i, a, b) for(ll i = a; i <= b ; ++i)
#define per(i, a, b) for(ll i = b ; i >= a ; --i)
#define MOD 998244353
#define traverse(u) for(int i = head[u]; ~i ; i = edge[i].nxt)
#define FOPEN freopen("C:\\Users\\tiany\\CLionProjects\\akioi\\data.txt", "rt", stdin)
#define FOUT freopen("C:\\Users\\tiany\\CLionProjects\\akioi\\dabiao.txt", "wt", stdout)
typedef long long ll;
typedef unsigned long long ull;
char buf[1 << 23], *p1 = buf, *p2 = buf, obuf[1 << 23], *O = obuf;
inline ll read() {
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
ll sign = 1, x = 0;
char s = getchar();
while (s > '9' || s < '0') {
if (s == '-')sign = -1;
s = getchar();
}
while (s >= '0' && s <= '9') {
x = (x << 3) + (x << 1) + s - '0';
s = getchar();
}
return x * sign;
#undef getchar
}//快读
void print(ll x) {
if (x / 10) print(x / 10);
*O++ = x % 10 + '0';
}
void write(ll x, char c = 't') {
if (x < 0)putchar('-'), x = -x;
print(x);
if (!isalpha(c))*O++ = c;
fwrite(obuf, O - obuf, 1, stdout);
O = obuf;
}
constexpr ll mod = 1e9 + 7;
ll quick_pow(ll base, ll expo){
ll ans = 1;
while(expo){
if(expo & 1) ans = ans * base % mod;
base = base * base % mod;
expo >>= 1;
}
return ans;
}
#define int ll
int n,m;
int a[limit];
vector<int>g[limit];
int fa[limit];
int sizes[limit];
int find(int x){
return fa[x] == x ? x : fa[x] = find(fa[x]);
}
void merge(int x,int y){
int fx = find(x);
int fy = find(y);
if(fx == fy) return;
fa[fx] = fy;
sizes[fy] += sizes[fx];
}
struct node{
ll val, id;
bool operator < (const node &rhs) const{
return val < rhs.val;
}
};
void solve(){
multiset<int> pa, pb;
cin >> n;
rep(i,1,n){
cin >> a[i];
fa[i] = i;
g[i].clear();
}
rep(i,1,n) {
cin >> a[i + 1 + n];
pa.insert(i);
pb.insert(i);
if (a[i] == a[i + 1 + n]) continue;
merge(a[i], a[i + 1 + n]);
}
map<int, int>mp;
rep(i,1,n){
mp[find(i)]++;
}
vector<ll> circle;
rep(i,1,n){
if(find(i) == i and mp[i] > 1){
circle.push_back(mp[i]);
}
}
ll ans = 0;
sort(circle.begin(), circle.end());
for(auto v : circle | ranges::views::reverse){
// cout<<v<<endl;
vector<int>t;
if(v & 1)--v;
rep(i,1,v){
if(i & 1){
t.push_back(*pa.begin());
pa.extract(t.back());
}else{
t.push_back(*pa.rbegin());
pa.extract(t.back());
}
// cout<<t[i]<<" ";
}
// cout<<endl;
rep(i,1, v - 1){
ans += abs(t[i] - t[i - 1]);
}
ans += abs(t.front() - t.back());
}
// cout<<endl;
cout<<ans<<endl;
};
int32_t main() {
#ifdef LOCAL
FOPEN;
// FOUT;
#endif
FASTIO
int kase;
cin>>kase;
while (kase--)
solve();
cerr << "Time elapsed: " << 1.0 * clock() / CLOCKS_PER_SEC << "s\n";
return 0;
}
感觉做点题挺好的,头脑清澈,很多问题并非无解,只不过自己题量不够,想不到这一层而已。