P8592 『JROI-8』颅脑损伤 2.0(加强版)
2023/8/9 把这篇烂题解翻了出来,优化了分析,没那么烂了
2023/8/12 对最新数据进行更新。
题目传送门:普通版 加强版
前言
赛时第 long long 就 AC 了。
困难版
O(n^2)
普通版扫一眼数据范围,按出题套路肯定是
排序 一般来说,需要给线段排个序,最基础的两种就是按前端排序和按后端排序。
考虑按后端排序,后端相等按前端排序,那么定义
状态转移 设左端点为
-
因为两条红线不可重叠,所以要保证
y_j<x_i 。 -
又要保证中间剩下的黑线(
j<k<i )至少接触一条红线,对于x_i \le y_k 的黑线可以接触到第i 条线段,所以只要保证x_i>y_k 的线段要接触第j 条线段。
设
- 那么加上第
i 条线段长度y_i-x_i ,总得("[ ]"内的表示条件)
统计 红色线段要把所有黑色线断覆盖,对于每一个
O(n^2) 代码
可结合注释李姐
提交记录
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int N=3*1e3,IINF=1e9+10;
const long long INF=1e18;
long long f[N+5],ans=INF;
struct stu{int x,y;}a[N+3];
bool cmp(stu a,stu b){//按后端排序,后端相等按前端排序
if(a.y==b.y) return a.x<b.x;
return a.y<b.y;
}
int main(){
int n,zmax=0;
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d%d",&a[i].x,&a[i].y);
zmax=max(zmax,a[i].x);//记录最大的左端点
}
sort(a+1,a+1+n,cmp);//排序
a[0].x=-IINF,a[0].y=-IINF;//j=0时第i条线段为第1条红线,不妨使x[0]=y[0]=-无限
for(int i=1;i<=n;i++){
int j;
for(j=i-1;a[j].y>=a[i].x;j--);//跳过y[j]>=x[i]的线段,这些线段能被第i条线段覆盖
int maxx=-IINF;f[i]=INF;
for(;j>=0&&a[j].y>=maxx;j--) f[i]=min(f[i],f[j]),maxx=max(maxx,a[j].x);//要满足中间的黑线对第i,j条线,至少触碰一条
f[i]+=a[i].y-a[i].x;//加上本条线段长度
if(a[i].y>=zmax) ans=min(ans,f[i]);//判断能否覆盖后面所有的线段,作为最后一条线段 更新
}
printf("%lld",ans);
}
O(n\log n)
26号,突然看见有困难版,便想用 双指针+线段树 优化,然后一直 WA,打了两个小时之后才发现被证伪了......别问为什么,问就是我太睿智了。普通版提交寄录
回归正题。其实上面
不难看出,第
换句话说,可转移的上一条红线是一段区间,而且右端点可以二分,从右端点往左拓展也满足单调性。
所以可以二分左右端点,右端点为
然后用线段树区间查询求出 f[j],然后插入 f[i]。
O(n\log n) 代码
提交记录
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=5e5,IINF=1e9+10;
const long long INF=1e18;
struct stu{int x,y;}a[N+5];
int maxx[N+5];
long long f[N+5],ans=INF,tree[5*N];
void updata(int l,int r,int rt,int x,long long y){//模板而已
if(l==r){
tree[rt]=y;
return;
}
int mid=l+r>>1;
if(x<=mid) updata(l,mid,rt<<1,x,y);
else updata(mid+1,r,rt<<1|1,x,y);
tree[rt]=min(tree[rt<<1],tree[rt<<1|1]);
}
long long ask(int l,int r,int rt,int x,int y){
if(x<=l&&r<=y) return tree[rt];
int mid=l+r>>1;long long ans=INF;
if(x<=mid) ans=min(ans,ask(l,mid,rt<<1,x,y));
if(y>mid) ans=min(ans,ask(mid+1,r,rt<<1|1,x,y));
return ans;
}
int main(){
int n,zmax=0;
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d%d",&a[i].x,&a[i].y);
zmax=max(zmax,a[i].x);
}
sort(a+1,a+1+n,
[](stu a,stu b){
if(a.y==b.y) return a.x<b.x;
return a.y<b.y;
});//一种奇妙的写法
for(int i=1;i<=4*n+100;i++) tree[i]=INF;
a[0].x=a[0].y=maxx[0]=-IINF;
updata(0,n,1,0,0);//注意下
for(int i=1;i<=n;i++){
maxx[i]=max(maxx[i-1],a[i].x);//更新最大的x
int l,r,x,y;
x=0,y=i;
while(y-x>1){
int mid=x+y>>1;
if(a[mid].y<a[i].x) x=mid;
else y=mid;
}r=x;//二分右端点
x=-1,y=r;
while(y-x>1){
int mid=x+y>>1;
if(a[mid].y>=maxx[r]) y=mid;//右端点r就是最后一个满足a[r].y<a[i].x的线段
else x=mid;
}l=y;//二分左端点
f[i]=ask(0,n,1,l,r)+a[i].y-a[i].x;
updata(0,n,1,i,f[i]);//要不断插入
if(a[i].y>=zmax) ans=min(ans,f[i]);
}
printf("%lld",ans);
}