专栏文章
题解 P13509 [OOI 2024] Almost Certainly
P13509题解参与者 4已保存评论 3
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 3 条
- 当前快照
- 1 份
- 快照标识符
- @minp3o3a
- 此快照首次捕获于
- 2025/12/02 06:02 3 个月前
- 此快照最后确认于
- 2025/12/02 06:02 3 个月前
提供一个考场思路。
分析
子任务 :
两个多重集完全等价时,需要的操作次数即为 。
几乎等价的条件允许了 中有一个元素不进行操作,即存在一对 不同,去掉后剩下的序列有解,即一一对应的 。答案为满足条件的 最大值。
对于每个前缀序列排序后询问,枚举 , 判断剩下的是否可行,总体时间复杂度 。
子任务 :
同样的对于每个前缀序列排序后询问。
时必定有解,且 时答案一定没有 时优秀,所以只考虑 。
选择对于位置在 之前的和位置在 之后的都是没有影响的,只需要考虑这两段序列的大小关系,即 。
固定 的同时依次从右向左扫描 判断条件的同时计算最大值,时间复杂度 。总体时间复杂度 。
子任务 :
同样的对于每个前缀序列排序后询问。
为了让 尽可能大, 也要尽可能的大。
所以找到每一个满足 的极长区间,所有极长区间答案的最大值一定是最大的, 扫一遍即可。
总体时间复杂度 或 ,取决于排序方式。
子任务 :
根据限制得到 ,保证前缀序列有序,无需再排序。
极长区间即为 ,最大值为 ,时间复杂度 。
子任务 :
保证前缀序列有序,无需再排序。
但该子任务并没有保证极长区间。
只需要维护和当前 相连的极长区间端点 值,新添加的 和端点相减更新答案,时间复杂度 。
综合以上可以获得 分。
CPP//the code is from chenjh
#include<bits/stdc++.h>
#define MAXN 1000005
using namespace std;
typedef long long LL;
int n;
int a[MAXN],b[MAXN];
void solve(){
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<=n;i++) scanf("%d",&b[i]);
LL ans=0;//完全等价的代价。
int ret=0,la,lb=1;//分别表示
for(int i=1;i<=n;i++){
ans+=a[i]-b[i];
if(i>1&&a[i]>=a[i-1]&&b[i]>=b[i-1]){//已经有序则无需排序。
a[i-1]<b[i]&&(lb=max(lb,i));//不满足条件,更新极长区间起点。
ret=max(ret,a[i]-b[lb]);
}
else{
inplace_merge(a+1,a+i,a+i+1),inplace_merge(b+1,b+i,b+i+1);//懒得手写排序,所以直接归并。
la=lb=ret=0;
for(int j=i;j>0;--j)
(j>=i||a[j]<b[j+1])&&(la=a[j]),ret=max(ret,la-b[j]),a[j-1]<b[j]&&(lb=max(lb,j));
}
printf("%lld ",ans-ret);
}
putchar('\n');
}
int main(){
int T;scanf("%d",&T);
while(T--) solve();
return 0;
}
子任务 :无特殊限制。
考试时败在了最后 分。
考虑将每个极长区间表示为 的区间。
如果两个区间有交集,则可以合并为同一个区间。
证明是容易的,如果区间 和 有交必然有 或 ,即边界满足条件可以进行合并。
如果区间被已有区间完全包含,就不必合并,也不必加入,因为不会产生更大的答案。
所求为所有区间的长度最大值。
于是直接用
std::set 维护区间,时间复杂度 。代码
CPP//the code is from chenjh
#include<bits/stdc++.h>
#define MAXN 1000005
using namespace std;
typedef long long LL;
typedef pair<int,int> PII;
int n;
int a[MAXN],b[MAXN];
void solve(){
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<=n;i++) scanf("%d",&b[i]);
LL ans=0;
int ret=0;
set<PII> S;
for(int i=1;i<=n;i++){
ans+=a[i]-b[i];
PII c(b[i],a[i]);
bool fl=1;
while(!S.empty()){
auto it=S.lower_bound(c);
if(it!=S.end()&&it->first==c.first&&it->second>=c.second){fl=0;break;}//区间被完全包含,合并后不产生贡献,退出。
if(it!=S.begin()&&prev(it)->second>=c.second&&prev(it)->first<=c.first){fl=0;break;}
if(it!=S.end()&&it->first<=c.second)
c.second=max(c.second,it->second),S.erase(it);//向右侧合并。
else if(it!=S.begin()&&(--it)->second>=c.first)
c.first=min(c.first,it->first),S.erase(it);//向左侧合并。
else break;//没有区间有交集了,退出。
}
if(fl) S.insert(c),ret=max(ret,c.second-c.first);
printf("%lld ",ans-ret);
}
putchar('\n');
}
int main(){
int T;scanf("%d",&T);
while(T--) solve();
return 0;
}
相关推荐
评论
共 3 条评论,欢迎与作者交流。
正在加载评论...