【BZOJ5288】【HNOI2018】游戏(乱搞?)
题面
题面自己到洛谷上看把
题解
考场上乱搞拿到了(90)分,简直不敢相信。
回家把代码再交了一份直接就(AC)了???
(O(n^2))的做法应该很容易想
对于每个点作为起点,暴力向左右两侧拓展,
看看它能够到达的区间就行了。
考虑怎么优化一下,发现如果拓展的时候走到了一个已经拓展完的节点
那么一定能够到达这个点能够到达的左右位置
因此,直接沿着当前点能够到达的最远左右位置跳跃一下,
中间就不用计算了。
我们发现这样很容易卡成(O(n^2))
于是我们倒着做,防止出题人故意卡正着做的
然后我就考场(90)分?洛谷/BZOJ AC了??
因为代码是考场代码,所以特别丑陋
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
#include<queue>
#include<set>
#include<map>
using namespace std;
#define ll long long
#define RG register
#define MAX 1001000
inline int read()
{
int x=0,t=1;char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')t=-1,ch=getchar();
while(ch>='0'&&ch<='9')x=x*10+ch-48,ch=getchar();
return x*t;
}
int n,m,Q;
int L[MAX],R[MAX],K[MAX];
namespace Task1//20pts
{
void Calc(int x)
{
int l=x,r=x,len=0;
while(233)
{
len=r-l;
while(l>1&&(!K[l-1]||(l<=K[l-1]&&K[l-1]<=r)))--l;
while(r<n&&(!K[r]||(l<=K[r]&&K[r]<=r)))++r;
if(r-l==len)break;
}
L[x]=l;R[x]=r;
}
void Solve()
{
for(int i=1;i<=n;++i)Calc(i);
while(Q--)
{
int S=read(),T=read();
if(L[S]<=T&&T<=R[S])puts("YES");
else puts("NO");
}
}
}
namespace pf
{
void Calc1(int x)
{
int l=x,r=x,len=0;
while(233)
{
len=r-l;
while(l>1&&(!K[l-1]||(l<=K[l-1]&&K[l-1]<=r)))r=max(r,L[l-1]),l=L[l-1];
while(r<n&&(!K[r]||(l<=K[r]&&K[r]<=r)))++r;
if(r-l==len)break;
}
L[x]=l;R[x]=r;
}
void Calc2(int x)
{
int l=x,r=x,len=0;
while(233)
{
len=r-l;
while(l>1&&(!K[l-1]||(l<=K[l-1]&&K[l-1]<=r)))
{
--l;
//if(L[l])r=max(r,R[l]),l=L[l];
}
while(r<n&&(!K[r]||(l<=K[r]&&K[r]<=r)))l=min(l,R[r+1]),r=R[r+1];
if(r-l==len)break;
}
L[x]=l;R[x]=r;
}
void Solve()
{
//for(int i=1;i<=n/2;++i)Calc1(i);
for(int i=n;i>=1;--i)Calc2(i);
while(Q--)
{
int S=read(),T=read();
if(L[S]<=T&&T<=R[S])puts("YES");
else puts("NO");
}
}
}
int main()
{
//freopen("game.in","r",stdin);
//freopen("game.out","w",stdout);
n=read();m=read();Q=read();
for(int i=1;i<=m;++i)
{
int x=read(),y=read();
K[x]=y;
}
if(n<=1000)Task1::Solve();
else pf::Solve();
return 0;
}