P7110 晚秋绝诗 题解
好有意思的题目啊。
出题人太厉害了。
思路
考虑一个结论:
我们将两个没插旗的点与中间的点称为一段,其中中间的点必须全部插旗。
那么这一段如果已知两座山的高度,就一定可以得知所有的高度。
考虑为什么。
加入这一段是 \(a\sim b\)。
\[\begin{cases}
h_a+h_{a+2}=2\times h_{a+1}\\
h_{a+1}+h_{a+3}=2\times h_{a+2}\\
h_{a+2}+h_{a+4}=2\times h_{a+3}\\
h_{a+3}+h_{a+5}=2\times h_{a+4}\\
\cdots\\
h_{b-2}+h_{b}=2\times h_{b-1}\\
\end{cases}
\]
总共可以列出 \(b-a-1\) 个方程。
而这一段总共只有 \(b-a+1\) 个未知量。
所以只需要知道其中两个即可。
这启发我们将整个序列按照是否插旗分段。
例如一个序列:
0110000111010
其中 \(0\) 表示没插旗,\(1\) 表示插旗。
所以整个序列可以分为如下几段。
\((1,4),(4,5),(5,6),(6,7),(7,11),(11,13)\)。
每个端点既是一个区间的左端点又是一个区间的右端点(除了最左和最右)。
很明显,端点属于极其特殊的点。
假如一个区间的所有点可以被求出,那么这个端点就相当于一个传递一样的给了别的区间。
那么我们只需要对于一个区间找到它两边的区间的端点是否能传递给他即可。
这可以让我们进行一个分类。
- 一个区间内有两个以上已知点,那么这是非常好的,整个区间也都知道了。
- 一个区间内有一个已知点且不是端点,那么发现这种段是起到了一个传递的作用,也就是只要另一边可以传递过来,那么它也可以传递出去,所以可知这种段有没有都无所谓。
- 其余的所有区间都可以归为一类,只需要判断相应的端点是否有雾即可。
这里感觉比其它题解少分了一类,简化了很多。
分完类,就有了一些很好的做法了。
我们使用两个 \(\text{set}\)。
一个维护连续段,一个维护第一类和第三类来查端点。
再使用线段树或树状数组维护区间已知点数量。
我采用了线段树。
代码也很好写。
Code
\(\text{2.23KiB}\)。
相较于其他的题解应该算很短的。
#include<bits/stdc++.h>
using namespace std;
#define fro(i,x,y) for(int i=(x);i<=(y);i++)
#define pre(i,x,y) for(int i=(x);i>=(y);i--)
const int N=5e5+10;
int n,m,x,op,a[N],b[N],t[N*4];
struct Node{
int l; mutable int r,op;
bool operator<(const Node&tmp)const
{ return l<tmp.l; }
};
set<Node> q1,q2;
#define mid ((l+r)>>1)
void upd(int p,int l,int r,int v,int k){
if(l==r)return t[p]=k,void();
if(mid>=v)upd(p*2,l,mid,v,k);
else upd(p*2+1,mid+1,r,v,k);
t[p]=t[p*2]+t[p*2+1];
}
int ask(int p,int l,int r,int ls,int rs){
if(ls<=l&&r<=rs)return t[p];int sum{};
if(mid>=ls)sum+=ask(p*2,l,mid,ls,rs);
if(mid<rs)sum+=ask(p*2+1,mid+1,r,ls,rs);
return sum;
}
void upd(Node tmp){
auto it1=q1.lower_bound(tmp);
auto it2=q2.lower_bound(tmp);
int s=ask(1,1,n,it1->l,it1->r);
it1->op=(s>=2?1:((a[it1->l]||a[it1->r]||!s)?2:3));
if(it1->op!=3){
if(it1->l!=it2->l)
q2.insert(*it1);
else it2->op=it1->op;
}
else if(it1->l==it2->l)
q2.erase(it2);
}
bool check(Node tmp){
int s=ask(1,1,n,tmp.l,tmp.r);
auto it1=q2.upper_bound(tmp);
auto it2=prev(q2.lower_bound(tmp));
if(it1!=q2.end()&&!a[tmp.r]&&(a[it1->l]||it1->op==1))s++;
if(it2->l<tmp.l&&!a[tmp.l]&&(a[it2->r]||it2->op==1))s++;
return s>=2;
}
void upd1(int x){
a[x]^=1,upd(1,1,n,x,a[x]);
auto it=q1.lower_bound({x,0,0});
if(x!=n&&!b[x])upd(*it);
if(it!=q1.begin())upd(*prev(it));
}
void ins(int x){
auto it2=q1.lower_bound({x,0,0});
auto it1=prev(it2);
if(it1->op!=3)q2.erase(*it1);
if(it2->op!=3)q2.erase(*it2);
it1->r=it2->r,q1.erase(*it2);
upd(*it1);
}
void del(int x){
auto it=prev(q1.lower_bound({x,0,0}));
if(it->op!=3)q2.erase(*it);
int l=it->l,r=it->r; q1.erase(it);
auto it1=q1.insert({l,x,0}).first;
auto it2=q1.insert({x,r,0}).first;
upd(*it1),upd(*it2);
}
void upd2(int x){b[x]^=1,(b[x]==1?ins(x):del(x));}
int ask(int x){
bool flag=0;
if(a[x]==1)return 1;
auto it=q1.lower_bound({x,0,0});
if(x!=n&&b[x]==0)flag|=check(*it);
if(it!=q1.begin())flag|=check(*prev(it));
return flag;
}
int main(){
ios::sync_with_stdio(0),cin.tie(0);
cin>>n>>m;
fro(i,1,n-1)
q1.insert({i,i+1,4}),
q2.insert({i,i+1,4});
fro(i,1,m){
cin>>op>>x;
if(op==1)upd1(x);
if(op==2)upd2(x);
if(op==3)
cout.rdbuf()->sputc((ask(x)?'1':'0')),
cout.rdbuf()->sputc('\n');
}
return 0;
}