文章目录
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个区域来进行判断该值是过大还是过小来缩小区间,每次要找的值必定在剩余区间的其中一个,所以该二分是可以得到正确答案的。
每次二分后需要通过划分区域来进行判断,划分区域有两种方法
- 遍历所有的元素,逐个进行区域的划分。
- 二分出满足第一个区域所在的上界,以及二分出第三个区域所在的下界,而第二个区域则在两个区域的中间,
(错误思路:二分元素划分的边界,因为要找的值所在区间不确定,所以无法得到最优解)
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次询问。询问有三种
- 阻塞两字符串相同位置t秒。
- 交换两个未阻塞位置的字符。
- 在忽略阻塞字符的情况下,两个字符串是否相等,若相等,则输出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;
}