Luogu P7110 晚秋绝诗
STO CXR ORZ
首先有一个前置的显然的结论,一座间峰可以让相邻的三座(边界就是两座)山峰的观测情况知二求三
容易发现对于一段连续的有雾有旗的山峰,我们如果知道了边上相邻的山峰的高度,这个信息是可以一路传递过去的
因此我们可以把所有有雾有旗的山峰删去,不再处理它们
首先如果询问的位置本身无雾那么很简单,接下来我们考虑这个山峰能被观测只有三种可能:
- 从左边的一段连续的山峰推出
- 从右边的一段连续的山峰推出
- 两边的位置可以分别由左右推出,同时这个位置本身有旗
现在只需要考虑如何判断一座山峰能否从左边的一段连续的山峰推出,其它的同理
考虑此时我们要传递信息,需要满足某座山本身就是无雾的或者是它之前的连续两座山推出了然后在前一座山有旗
因此我们现在只需要判断一个位置左边的最近的连续两个无雾的位置和它之前能连续传递信息的位置的前后关系即可
接下来考虑实现,我们考虑用四个set
分别维护所有存在的位置,从左边推过来会被隔断的点,从右边推过来会被隔断的点,所有连续的两个无雾的位置
注意我们无法对于每个点求出能从左能到达的最近点是什么,因为这样修改会涉及一整个区间,因此不妨把所有断点维护出来,直接在上面找即可
具体的实现看代码,细节还是比较多的,复杂度\(O(n\log n)\)
#include<cstdio>
#include<set>
#define RI register int
#define CI const int&
using namespace std;
typedef set <int>:: iterator SI;
const int N=500005;
int n,m,opt,x; bool fog[N],flag[N]; set <int> E,P,L,R;
//E: Exist Positions; P: Pair Positions; L,R: Nearest Unreachable Positions;
#define Pre(s,x) (*--s.lower_bound(x))
#define Nxt(s,x) (*s.upper_bound(x))
inline void FP(CI x)
{
if (!x||x>=n) return; if (P.erase(x),fog[x]&&flag[x]) return;
if (!fog[x]&&!fog[Nxt(E,x)]) P.insert(x);
}
inline void FL(CI x)
{
if (!x||x>n) return; if (L.erase(x),fog[x]&&flag[x]) return;
if (!(!fog[x]||flag[Pre(E,x)])) L.insert(x);
}
inline void FR(CI x)
{
if (!x||x>n) return; if (R.erase(x),fog[x]&&flag[x]) return;
if (!(!fog[x]||flag[Nxt(E,x)])) R.insert(x);
}
inline bool QL(CI x) { return Pre(L,x+1)<Pre(P,x); }
inline bool QR(CI x) { return Nxt(R,x-1)>Nxt(P,x-1); }
int main()
{
RI i; for (scanf("%d%d",&n,&m),i=0;i<=n+1;++i)
fog[i]=1,L.insert(i),R.insert(i),E.insert(i);
for (P.insert(0),P.insert(n),i=1;i<=m;++i)
switch (scanf("%d%d",&opt,&x),opt)
{
case 1:
if (fog[x]^=1,flag[x]) { if (fog[x]) E.erase(x); else E.insert(x); }
FP(x); FP(Pre(E,x)); FL(x); FR(x); FL(Nxt(E,x)); FR(Pre(E,x)); break;
case 2:
if (flag[x]^=1,fog[x]) { if (flag[x]) E.erase(x); else E.insert(x); }
FP(x); FP(Pre(E,x)); FL(x); FR(x); FL(Nxt(E,x)); FR(Pre(E,x)); break;
case 3:
/*printf("DUBUG for question %d:\n",i);
puts("E:"); for (SI it=E.begin();it!=E.end();++it) printf("%d ",*it); putchar('\n');
puts("P:"); for (SI it=P.begin();it!=P.end();++it) printf("%d ",*it); putchar('\n');
puts("L:"); for (SI it=L.begin();it!=L.end();++it) printf("%d ",*it); putchar('\n');
puts("R:"); for (SI it=R.begin();it!=R.end();++it) printf("%d ",*it); putchar('\n');*/
if (!fog[x]) { puts("1"); break; } if (!flag[x]) { puts(QL(x)||QR(x)?"1":"0"); break; }
bool cl=QL(x-1),cr=QR(x+1); if ((cl||!fog[Pre(E,x)])&&(cr||!fog[Nxt(E,x)])) { puts("1"); break; }
puts((cl&&flag[Pre(E,x)])||(cr&&flag[Nxt(E,x)])?"1":"0"); break;
}
return 0;
}
辣鸡老年选手AFO在即