Codeforces Round 959 sponsored by NEAR (Div. 1 + Div. 2)

题目链接:Codeforces Round 959 sponsored by NEAR (Div. 1 + Div. 2)

总结:Bwa两发,C读假题。发挥很一般,补题感觉到E都是能做的,红温。

A. Diverse Game

fag:签到

B. Fun Game

fag:位运算 + 思维

Description:给定一个两个01字符串s,t,对于s,每次可以选择一个区间l,r,令sisil+1替换si,l<=i<=r。问能否将

s转化为t

Solution:1与(0, 1)异或会将其改变, 0与任何数异或为0

  • 那么假设s串中第一个不同的位置前面有1,那么后面的数都可以与这个数异或,从而变得相等。如果没有但是si
    1,则可以自己变为0,或者不行。
  • 即看st串中谁先出现1,因为后面的数可以依靠的数改变。

Competing:有点慌乱,乱交了两发。难绷

void solve() {
int n;
cin >> n;
string s, t;
cin >> s >> t;
for (int i = 0; i < n; i++) {
if (s[i] == '1') {
cout << "Yes\n";
return;
}
if (t[i] == '1') {
cout << "No\n";
return;
}
}
cout << "Yes\n";
}

C. Hungry Games

fag: 前缀和 + 二分

Description:给定n个数和一个x。选择一个区间l,r,玩家的毒性g,初始为0,首先g+=ai;如果g<=x游戏继续,否则g=0,游戏继续。求有多少个子区间使玩家毒性最终不为0

Solution:一般求区间个数,我会考虑前缀和或者双指针,因为所求区间个数往往很大。

  • 我们简单模拟下,开始g不断增大,然后变为0,然后再增大。那么对于变为0之后再增大这部分是不是等价于以这个位置为起点,进行相同的操作。显然我们暴力求解会计算很多相同的状态,考虑优化。
  • 我们用cnt[i]表示以这个点为起点的方案数。那么cnt[i]=(ji)+cnt[j+1],其中j是变为0的位置。
  • 我们需要用后面的位置更新前面的位置,所以我们从后面开始计算。那么怎么计算j的位置呢?因为g是递增的,考虑二分。
void solve(){
int n, x;
cin >> n >> x;
vector<int> a(n + 5, 1e18), s(n + 5, 1e18);
s[0] = 0;
for (int i = 1; i <= n; i ++){
cin >> a[i];
s[i] = s[i - 1] + a[i]; // 前缀和
}
vector<int> cnt(n + 5);
LL ans = 0;
for (int i = n; i; i --){
// 我们从后往前计算答案,cnt[i]表示以i为起点有多少符合条件的子串
// 我们从i点开始找到第一个不符合条件的点(第一个子串的结尾)
int idx = lower_bound(s.begin(), s.end(), s[i - 1] + x + 1) - s.begin();
// 但是它在idx处清0了,因此从idx + 1起又开始从新计算答案(但是我们已经记录下来了)
cnt[i] += cnt[idx + 1] + (idx - i);
ans += cnt[i];
}
cout << ans << endl;
}

D. Funny Game

fag:并查集 + 鸽巢原理

Solution:给定n个数,有n1操作,每次操作可以选择两个数au,av,满足|auav|x整除(假设这是第x次操作)然后将这两个点相连,问最后能否形成一个连通图,如果能给出操作序列。

Description:样例全是Yes,考虑是否一定有解。然后从后往前操作比从前往后操作更优(可能满足后面操作的数被前面选中)。假设当前有n个数,对n1取余后,根据鸽巢原理一定有两个数余数相等。因此能够选出符合条件的数。然后将这两个数合并,还剩n1个数,对n2取余,同理是有解的。

  • 考虑每次如何保证每次取的数是两个不同的连通块,使用并查集维护信息,每次只取代表节点即可(f[x]==x)。
int f[N];
void init(){
for (int i = 0; i < N; i ++)
f[i] = i;
}
int find(int x){
if (x != f[x])
f[x] = find(f[x]);
return f[x];
}
bool merge(int x, int y){
int fx = find(x), fy = find(y);
if (fx == fy)
return false;
f[fx] = fy;
return true;
}
void solve(){
cin >> n;
vector<int> a(n + 1);
for (int i = 1; i <= n; i ++){
cin >> a[i];
}
vector<pii> ans(n);
for (int i = n - 1; i; i --){
vector<int> p(n, -1);
for (int j = 1; j <= n; j ++){
if (find(j) == j){
int r = a[j] % i;
if (p[r] != -1){
merge(p[r], j);
ans[i] = {p[r], j};
break;
}
else
p[r] = j;
}
}
}
cout << "Yes\n";
for (int i = 1; i < n; i ++){
cout << ans[i].fi << " " << ans[i].se << endl;
}
}

E. Wooden Game

fag:贪心 + 二进制

Description:有k棵树,每次操作可以任意删去一颗子树,可以操作任意次。求删去子树大小按位或的最大值。

Solution:题目求按位或的最大值,我们显然要想到位运算。

  • 对于每一棵树,如果他的大小为ai,那么我们可以取[0,ai]的任意一个值出来运算,只需要删去一定数量的叶子节点。

  • 等价于每个数的可以为[0,ai],求它们异或的最大值。

  • 我们从最高位开始一位一位看,如果该位有两个数都是1,那么该位可以取1,后面的所有位都可以取1。将其中一个数减1即可。

  • 该位只有一个数为1,那么该位取1

void solve(){
cin >> n;
vector<int> a(n);
vector<int> cnt(32);
for (int i = 0; i < n; i ++){
cin >> a[i];
for (int j = 0; j < a[i] - 1; j ++){
int x;
cin >> x;
}
for (int j = 31; ~j; j --){
if ((a[i] >> j) & 1)
cnt[j] ++;
}
}
int ans = 0;
for (int i = 31; ~i; i --){
if (cnt[i] >= 2){
ans |= (1LL << (i + 1)) - 1;
break;
}
else if (cnt[i]){
ans |= 1LL << i;
}
}
cout << ans << endl;
}
posted @   Sakura17  阅读(20)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示