Codeforces Round 897 (Div. 2) 考试总结

前言#

这次打得很好,相较于 div3 的脑残题和签到题来说,div2 的思维难度更加的大。同时还有除传统题外,其他的题型出现。比如交互题等。这次能在考场上想出三道较于之前是有很大的进步的。

赛时实况:

A B C D E1 E2 F
× × × ×

赛后改题情况:

A B C D E1 E2 F
× ×

感觉 D、F 不是很会。

A. green_gold_dog, array and permutation#

Problem#

给定一个长度为 n 的数列 a,现在需要找到一个排列 b,使得 aibi 互不相同。若有多种答案,输出其中一种即可。

Solve#

首先,b 数组是一个排列,考虑排列的性质:没有重复元素,每个数只出现一次,所有数的值小于数列长度 n

现在要使 aibi 互不相同,其实就是在说 aibi 在数轴上的分布要相对分散。我们大胆猜测:最大的 ai 匹配最小的 bi,次大的 ai 匹配次小的 bi,最小的 ai 匹配最大的 bi

可以用反证法证明:假如 i<j, ci=cj,此时 aiaj,bi<bj。则有:ci=aibi>ajbj=cj。产生矛盾,故 i<j, cicj,证毕。

时间复杂度 O(Tnlogn)

Code#

#include <bits/stdc++.h>
#define int long long
#define H 19260817
#define rint register int
#define For(i,l,r) for(rint i=l;i<=r;++i)
#define FOR(i,r,l) for(rint i=r;i>=l;--i)
#define MOD 1000003
#define mod 1000000007

using namespace std;

namespace Read {
  template <typename T>
  inline void read(T &x) {
    x=0;T f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    x*=f;
  }
  template <typename T, typename... Args>
  inline void read(T &t, Args&... args) {
    read(t), read(args...);
  }
}

using namespace Read;

void print(int x){
  if(x<0){putchar('-');x=-x;}
  if(x>9){print(x/10);putchar(x%10+'0');}
  else putchar(x+'0');
  return;
}

const int N = 4e4 + 10;

int T, n, a[N], p[N], Ans[N]; 

bool cmp(int x, int y) {
  return a[x] > a[y];
}

void solve() {
  read(n);
  For(i,1,n) read(a[i]), p[i] = i;
  sort(p + 1, p + n + 1, cmp);
  For(i,1,n) Ans[p[i]] = i;
  For(i,1,n) cout << Ans[i] << ' ';
  cout << '\n';
}

signed main() {
  read(T);
  while(T--) {
    solve();
  }
  return 0;
}

B. XOR Palindromes#

Problem#

给定一个 n 位二进制 01 串。一个数 x 是好的定义为:存在一个长度为 n 二进制 01l,使得按位操作 sili 后的串为回文串。

现有长度为 n+1 答案串 t,第 i(0in+1) 位为 1 表示数字 i 是好的,为 0 表示数字 i 是不好的。求 t

Solve#

首先,肯定是从小到大找答案。考虑最小的,合法(“好的”)的数 x 为多少。也就是求将原串变为回文串的最小步数。

做法是从串的中点向左右扩展。若两数不同,则耗费一步数更改其中的一位。具体是哪一位不需要管。因为我们只需要将其变为回文串,不需要关心其形态。

然后知道最小的 x,考虑怎样将答案拓展。不难发现,当 n 为奇数时,介于 xnx 之间的数 y 都是“好的”;当 n 为偶数时,介于 xnx 之间的数 yyx 为偶数都是“好的”。

证明很简单,当 n 为偶数时,每次在中点的两边对称的地方同时更改不会改变其回文的状态。也就是说,当 yx 为偶数且 y[x,nx] 时,y 是好的。奇数时同理,不过在中心改也不会改变其回文的状态,这样可以组合出 [x,nx] 的所有数,自然其中的所有数也都是“好的”。

最后模拟即可。

时间复杂度 O(Tn)

Code#

#include <bits/stdc++.h>
#define int long long
#define H 19260817
#define rint register int
#define For(i,l,r) for(rint i=l;i<=r;++i)
#define FOR(i,r,l) for(rint i=r;i>=l;--i)
#define MOD 1000003
#define mod 1000000007

using namespace std;

namespace Read {
  template <typename T>
  inline void read(T &x) {
    x=0;T f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    x*=f;
  }
  template <typename T, typename... Args>
  inline void read(T &t, Args&... args) {
    read(t), read(args...);
  }
}

using namespace Read;

void print(int x){
  if(x<0){putchar('-');x=-x;}
  if(x>9){print(x/10);putchar(x%10+'0');}
  else putchar(x+'0');
  return;
}

const int N = 1e5 + 10;

int T, n;

char a[N];

void solve() {
  read(n);
  For(i,1,n) cin >> a[i];
  if(n & 1) {
    int l = (1 + n) >> 1, r = l, ans1 = 0;
    while(l >= 1) {
      if(a[l] != a[r]) ans1++;
      l--, r++;
    }
    For(i,0,n) {
      if(i >= ans1 && i <= n - ans1) cout << 1;
      else cout << 0; 
    }
    cout << '\n';
  } else {
    int l = n >> 1, r = l + 1, ans1 = 0;
    while(l >= 1) {
      if(a[l] != a[r]) ans1++;
      l--, r++;
    }
    For(i,0,n) {
      if(i >= ans1 && i <= n - ans1 && (i - ans1) % 2 == 0) cout << 1;
      else cout << 0; 
    }
    cout << '\n';
  }
}

signed main() {
  read(T);
  while(T--) {
    solve();
  }
  return 0;
}

C. Salyg1n and the MEX Game#

Problem#

交互题。给定一个 n 个元素的集合 S,元素记为 si(1in)。每一回合,你可以选择一个数 x(0x109) 插入集合中,交互机会选择一个 x 的数,并将其从 S 中删除,保证总回合数不超过 2n1

游戏最后的答案为 MEX,你要最大化这个结果,交互机要最小化这个结果。设 R 为你和交互机操作的最佳策略下的答案,请你规定一个策略,使最终答案至少为 R

Solve#

脑瘫交互。

注意到每次交互机会选择一个 x 的数,并将其从 S 中删除。那么就很有可能删除掉你的 MEX。不过,你是先手。那么就能将问题转化:你先选择一个数插入,再变为交互机先手,你后手。

你先选择并插入的的数肯定是数列的 MAX,因为你要最大化 MEX。然后交互机删什么,你就填什么即可。

时间复杂度 O(Tn)

Code#

#include <bits/stdc++.h>
#define int long long
#define H 19260817
#define rint register int
#define For(i,l,r) for(rint i=l;i<=r;++i)
#define FOR(i,r,l) for(rint i=r;i>=l;--i)
#define MOD 1000003
#define mod 1000000007

using namespace std;

namespace Read {
  template <typename T>
  inline void read(T &x) {
    x=0;T f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    x*=f;
  }
  template <typename T, typename... Args>
  inline void read(T &t, Args&... args) {
    read(t), read(args...);
  }
}

using namespace Read;

void print(int x){
  if(x<0){putchar('-');x=-x;}
  if(x>9){print(x/10);putchar(x%10+'0');}
  else putchar(x+'0');
  return;
}

const int N = 1e5 + 10;

int T, n, y, a[N];

void solve() {
  read(n);
  For(i,1,n) cin >> a[i];
  sort(a + 1, a + n + 1);
  int MEX = 0;
  For(i,1,n) {
    if(a[i] == MEX) MEX++;
  }
  cout << MEX << '\n';
  fflush(stdout);
  while(cin >> y) {
    if(y == -1) return ;
    cout << y << '\n';
    fflush(stdout);
  }
}

signed main() {
  read(T);
  while(T--) {
    solve();
  }
  return 0;
} 

这可能是我人生第一次自己做的交互题了。

D. Cyclic Operations#

Problem#

(咕咕咕...)

Solve#

(咕咕咕...)

Code#

(咕咕咕...)

E1. Salyg1n and Array (simple version)#

Problem#

交互题。交互库有一个长度为 n 的数列 a,求其所有元素的异或和,每次你可以选择一个 i,询问 [i,i+k1] 中的异或和,并将其翻转。询问不能超过 100 次。保证 nk 均为偶数

1knk22500

Solve#

nmodk=0 时,直接分成 nk 然后区间覆盖询问就行了。
nmodk0 时,[1,knk] 直接区间覆盖询问即可,对于 [knk+1,n] ,选择 [knkk+2,nk+1] 的所有 i 并且询问即可。

证明:原数组记为 ai,询问 knkk+2 时,knk+1 会被计算进去,而 [knkk+2,knk] 总共会被计算两次,即两次异或操作,抵消。以此类推,最后,[knk+1,n] 的所有数将会被计算一次。而 [knkk+2,knk] 将会被计算 (nmodk)+1 次,而 nmodk 为偶数,则 (nmodk)+1 为奇数。则原数列每个数都可以不重不漏的计算一次。

区间覆盖询问时,操作数为 nk,题目数据范围给出 1knk22500,所以 nkkn50,所以询问数最多不会超过 50 次。而 (nmodk)+1=nnk+150,所以总询问数不会超过 100 次,可以通过本题。

时间复杂度 O(Tn)

Code#

#include <bits/stdc++.h>
#define ll long long
#define H 19260817
#define rint register int
#define For(i,l,r) for(rint i=l;i<=r;++i)
#define FOR(i,r,l) for(rint i=r;i>=l;--i)
#define MOD 1000003
#define mod 1000000007

using namespace std;

namespace Read {
  template <typename T>
  inline void read(T &x) {
    x=0;T f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    x*=f;
  }
  template <typename T, typename... Args>
  inline void read(T &t, Args&... args) {
    read(t), read(args...);
  }
}

using namespace Read;

void print(int x){
  if(x<0){putchar('-');x=-x;}
  if(x>9){print(x/10);putchar(x%10+'0');}
  else putchar(x+'0');
  return;
}

int n, T, k, res;

int ask(int i) {
  cout << "? " << i << '\n';
  fflush(stdout);
  int x; cin >> x;
  return x;
}

void solve() {
  res = 0; 
  cin >> n >> k;
  int i;
  for (i = 1; i + k - 1 <= n; i += k) {
    res ^= ask(i);
  } 
  if(n % k == 0) {
    cout << "! " << res << '\n';
    fflush(stdout);
  } else {
    res ^= ask(i - k + (n % k) / 2);
    res ^= ask(n - k + 1);
    cout << "! " << res << '\n';
    fflush(stdout);
  }
  return ;
}

signed main() {
  cin >> T;
  while(T--) {
    solve(); 
  }
  return 0;
}

E2. Salyg1n and Array (hard version)#

Problem#

交互题。交互库有一个长度为 n 的数列 a,求其所有元素的异或和,每次你可以选择一个 i,询问 [i,i+k1] 中的异或和,并将其翻转。询问不能超过 57 次。保证 nk 均为偶数

Solve#

我们发现 E1 的程序不高效率的原因在于它有很多的冗余运算。比如,最后的剩余段会被询问 (nmodk)+1 次,这是很不高效的。我们发现前面的区间覆盖询问 nk 次是无法避免的。这时候,留给我们的剩余询问数就只剩下了 7 个。所以我们要高效的处理剩余段的询问。

我们考虑将 [knk+1,n] 折半,一半的长度记为 l。将左半段区间和 [knk+1l,knk] 一起询问,然后原数组 [knk+1l,knk] 将会被翻转到折半区间的左半段区间,再与右半段区间询问一次。这样,原本 [knk+1,n] 将会被询问一次。[knk+1l,knk] 总共会被询问 3 次,贡献一次答案。刚好将原数组的所有元素包含。

这样最多只需要询问 52 即可,可以通过此题。

Code#

#include <bits/stdc++.h>
#define ll long long
#define H 19260817
#define rint register int
#define For(i,l,r) for(rint i=l;i<=r;++i)
#define FOR(i,r,l) for(rint i=r;i>=l;--i)
#define MOD 1000003
#define mod 1000000007

using namespace std;

namespace Read {
  template <typename T>
  inline void read(T &x) {
    x=0;T f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    x*=f;
  }
  template <typename T, typename... Args>
  inline void read(T &t, Args&... args) {
    read(t), read(args...);
  }
}

using namespace Read;

void print(int x){
  if(x<0){putchar('-');x=-x;}
  if(x>9){print(x/10);putchar(x%10+'0');}
  else putchar(x+'0');
  return;
}

int n, T, k, res;

int ask(int i) {
  cout << "? " << i << '\n';
  fflush(stdout);
  int x; cin >> x;
  return x;
}

void solve() {
  res = 0; 
  cin >> n >> k;
  int i;
  for (i = 1; i + k - 1 <= n; i += k) {
    res ^= ask(i);
  } 
  if(n % k == 0) {
    cout << "! " << res << '\n';
    fflush(stdout);
  } else {
    res ^= ask(i - k + (n % k) / 2);
    res ^= ask(n - k + 1);
    cout << "! " << res << '\n';
    fflush(stdout);
  }
  return ;
}

signed main() {
  cin >> T;
  while(T--) {
    solve(); 
  }
  return 0;
}

F. Most Different Tree#

Problem#

(咕咕咕...)

Solve#

(咕咕咕...)

Code#

(咕咕咕...)

作者:Daniel-yao

出处:https://www.cnblogs.com/Daniel-yao/p/17702654.html

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

posted @   Daniel_yzy  阅读(250)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
more_horiz
keyboard_arrow_up dark_mode palette
选择主题
menu
点击右上角即可分享
微信分享提示