Codeforces Round 878 (Div. 3) BDE题解

D. Wooden Toy Festival

在这里插入图片描述
在这里插入图片描述在这里插入图片描述

问题分析

给定n个数,可以选定3个数作为基准数,对于这n个数每个数可以选择一个基准数进行作差,问在选定某3个基准数后最小的n个数作差的最大值为多少,输出该值。

问题建模

1.分析所求

若先将序列元素排序,然后选3个数x1,x2,x3作为基准数,其中x1<=x2<=x3。该基准点作差的最大值,则实际是在划分了3个区域,分别为1~ (x1+x2)/2 ,(x1+x2)/2 ~ (x2+x3)/2 ,(x2+x3)/2 ~ n后,取三个区域区间最大值t的 ⌈ t / 2 ⌉ \lceil t/2 \rceil t/2

而题目要求输出最小的最大作差值,则我们可以考虑二分一个作差值m,然后通过尝试划分3个区域来进行判断该值是过大还是过小来缩小区间,每次要找的值必定在剩余区间的其中一个,所以该二分是可以得到正确答案的。

每次二分后需要通过划分区域来进行判断,划分区域有两种方法

  1. 遍历所有的元素,逐个进行区域的划分。
  2. 二分出满足第一个区域所在的上界,以及二分出第三个区域所在的下界,而第二个区域则在两个区域的中间,

(错误思路:二分元素划分的边界,因为要找的值所在区间不确定,所以无法得到最优解)

2.代码

#include<bits/stdc++.h>

#define x first
#define y second
#define C(i) str[0][i]!=str[1][i]
using namespace std;
typedef unsigned long long ULL;
typedef long long LL;
typedef pair<int, int> PII;
const int N=2e5+10,INF=0x3f3f3f3f;
int a[N];
int n;

///划分区域方法2二分上下界
bool check(int m){
    int x,y;
    x=upper_bound(a,a+n,a[0]+2*m)-a;///第一个区域上界
    y=lower_bound(a,a+n,a[n-1]-2*m)-a-1;///第三个区域下界
    ///若有一个区域过大,则返回true,继续缩小区域
    if(y<x||a[y]-a[x]<=2*m) return true;
    else return false;
}

///划分区域方法1,遍历所有元素
bool check1(int m){
    int l=0,r=0;///l,r所包含的为一个区域
    for(int i=0;i<3;i++){
        while(r<n&&a[r]-a[l]<=2*m) r++;
        ///当上一个区域划分结束时,l,r更替,划分下一个区域
        l=r;
    }
    ///若l==n时,则说明区域过大,需要继续
    return  l==n; 
}

void solve() {
    cin >>n;
    for(int i=0;i<n;i++)   scanf("%d",&a[i]);
    sort(a,a+n);
    int l=0,r=1e9;
    while(l<r){
        int mid=(l+r)>>1;
        if(check(mid))  r=mid;
        else l=mid+1;
    }
    cout <<l <<"\n";
}

int main() {
    int t=1;
    cin >>t;
    while (t--)  solve();
    return 0;
}

E. Character Blocking

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

问题建模

给定两个等长的字符串,由小写字母组成,以及阻塞时间t,以及q次询问。询问有三种

  1. 阻塞两字符串相同位置t秒。
  2. 交换两个未阻塞位置的字符。
  3. 在忽略阻塞字符的情况下,两个字符串是否相等,若相等,则输出YES,否则输出NO。

问题分析

1.分析所求

实际上对于q次查询,我们需要输出的仅有第三种查询,即忽略阻塞字符的情况下,两个字符串是否相等。若我们每次直接使用字符串来进行比较,则对于前两种查询,我们每次都要花费O(n) 的时间来处理字符串,则对于q次查询可能会超时。

则我们需要通过其他的信息来判断两个字符串是否相等 ,通过直接比较字符串的方法,实际上是在判断两个字符串的每一个字符是否相等,则我们可以维护两个字符串中对应位置字符个数不同的个数(称为坏点数),由于每次阻塞和交换字符后两字符串对应位置是否可比都是统一的,所以仅维护该信息就可以满足判断的要求。

2.分析其余查询操作对结果的影响

对于第一种查询操作在阻塞某一对位置的字符后,若该对字符时不匹配的,则坏点数加1,并用一个队列存入该位置以及其解除阻塞的时间,每一次新的查询前,查看是否有解除阻塞的字符,有则判断该位置是否有坏点

对于第二种操作,分别考虑两个字符交换前所在位置是是否产生坏点,若有则当前坏点数-1,在交换后,再次判断所在位置是否有坏点,有则坏点数加1(注意:题目中交换位置可能为不同行,则不能在未交换时就进行与新位置字符的比较)

这样计算初始坏点的时间为O(n),维护坏点的则在每次查询时进行一次即O(1),则q次查询下来时间复杂度为O(n+q),不会超时。

3.代码

#include<bits/stdc++.h>

#define x first
#define y second
using namespace std;
typedef unsigned long long ULL;
typedef long long LL;
typedef pair<int, int> PII;
const int N=1e5+10,Mod=998244353;
#define C(i) str[0][i]!=str[1][i]

void solve() {
    int t,q;
    string str[2];
    cin >>str[0] >>str[1] >>t >>q;
    int bad=0;///坏点数
    for(int i=0;i<str[0].size();i++){
        if(C(i))    bad++;
    }
    queue<PII> qu;

    for(int i=1;i<=q;i++){
        int op;
        cin >>op;
        ///释放阻塞字符
        if(qu.size()&&qu.front().y<=i){
            if(C(qu.front().x))    bad++;
            qu.pop();
        }
        if(op==1){
            int pos;
            cin >>pos;
            pos--;
            if(C(pos))    bad--;
            ///添加阻塞字符
            qu.push({pos,i+t});
        }else if(op==2){
            int s1,s2,p1,p2;
            cin >>s1 >>p1 >>s2 >>p2;
            s1--,s2--,p1--,p2--;
            if(C(p1))  bad--;
            if(C(p2))  bad--;
            swap(str[s1][p1],str[s2][p2]);
            if(C(p1))  bad++;
            if(C(p2))  bad++;
        }else{
            cout <<(bad?"NO":"YES") <<"\n";
        }
    }
}

int main() {
    int t=1;
    cin >>t;
    while (t--)  solve();
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值