传送门:bzoj4723
题解
看到此题,本蒟蒻去重温了一下fb…(还是那么菜)
有两种方法(一种比较直观,一种比较玄学)。
直观的:
考虑对于每个坐标,我们飞到
(x,y)
(
x
,
y
)
所点击的次数实际上是固定的(
(x+y)/2
(
x
+
y
)
/
2
),而且我们也只能飞到横纵坐标奇偶性一样的位置。而且这个小鸟它可以打洞!(飞到纵坐标为负的位置),所以只要飞过最后一个障碍就无所谓了。
我们先把每个障碍的坐标范围更新一下(横纵坐标奇偶性不一样的,如果是上端点就减一下,下端点就加一下)。显然,对于每个障碍,穿过这个障碍可以到达的坐标范围是一个逆时针旋转了九十度的等腰梯形。我们不断传递下去就好了。
代码如下:
#include<bits/stdc++.h>
using namespace std;
const int N=5e5+10;
int n,X,x[N],l[N],r[N],tp,L,R;
inline int rd()
{
char ch=getchar();int x=0,f=1;
while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
while(isdigit(ch)){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
return x*f;
}
int main(){
n=rd();rd();
for(int i=1;i<=n;++i){
x[i]=rd();l[i]=rd();r[i]=rd();
l[i]++;r[i]--;
if((l[i]&1)^(x[i]&1)) l[i]++;
if((r[i]&1)^(x[i]&1)) r[i]--;
}
for(int i=1;i<=n;++i){
R+=x[i]-x[i-1];
L-=x[i]-x[i-1];
if((R&1)^(x[i]&1))R--;
if((L&1)^(x[i]&1))L++;
R= R>r[i]? r[i]:R;
L= L<l[i]? l[i]:L;
if(L>R){printf("NIE\n");return 0;}
}
printf("%d\n",(x[n]+L)>>1);
}
另一种玄学做法(来自PoPoQQQ):
贪心
预处理f[i]表示第
i...n
i
.
.
.
n
个柱子中
a[i]−x[i]
a
[
i
]
−
x
[
i
]
的最大值,显然如果我在穿过第
i−1
i
−
1
个柱子后的某一时刻
y−x<=f[i]
y
−
x
<=
f
[
i
]
,那么我就GG了
所以我先下降到这条线上方,然后再一直点就好了。
时间复杂度
O(n)
O
(
n
)
感性理解
//here is another one
#include<bits/stdc++.h>
using namespace std;
const int N=5e5+10;
int n,X,x[N],l[N],r[N],lim[N],tp,L,R,ans;
inline int rd()
{
char ch=getchar();int x=0,f=1;
while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
while(isdigit(ch)){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
return x*f;
}
int main(){
n=rd();rd();
for(int i=1;i<=n;++i){
x[i]=rd();l[i]=rd();r[i]=rd();
l[i]++;r[i]--;
lim[i]=l[i]-x[i];if(lim[i]&1) lim[i]++;
}
for(int i=n-1;i>=1;i--) lim[i]=max(lim[i],lim[i+1]);
for(int i=1;i<=n;++i){
tp=(R-L)-lim[i]>>1;
if(tp<0) tp=0;
tp=x[i]-L-tp;
if(tp<0) tp=0;
R+=tp;R-=x[i]-L-tp;
L=x[i];
if(R>r[i] || R<l[i]){printf("NIE\n");return 0;}
ans+=tp;
}
printf("%d\n",ans);
}