P7110 晚秋绝诗 题解

好有意思的题目啊。

出题人太厉害了。

思路#

考虑一个结论:

我们将两个没插旗的点与中间的点称为一段,其中中间的点必须全部插旗。

那么这一段如果已知两座山的高度,就一定可以得知所有的高度。

考虑为什么。

加入这一段是 ab

{ha+ha+2=2×ha+1ha+1+ha+3=2×ha+2ha+2+ha+4=2×ha+3ha+3+ha+5=2×ha+4hb2+hb=2×hb1

总共可以列出 ba1 个方程。

而这一段总共只有 ba+1 个未知量。

所以只需要知道其中两个即可。

这启发我们将整个序列按照是否插旗分段。

例如一个序列:

0110000111010

其中 0 表示没插旗,1 表示插旗。

所以整个序列可以分为如下几段。

(1,4),(4,5),(5,6),(6,7),(7,11),(11,13)

每个端点既是一个区间的左端点又是一个区间的右端点(除了最左和最右)。

很明显,端点属于极其特殊的点。

假如一个区间的所有点可以被求出,那么这个端点就相当于一个传递一样的给了别的区间。

那么我们只需要对于一个区间找到它两边的区间的端点是否能传递给他即可。

这可以让我们进行一个分类。

  1. 一个区间内有两个以上已知点,那么这是非常好的,整个区间也都知道了。
  2. 一个区间内有一个已知点且不是端点,那么发现这种段是起到了一个传递的作用,也就是只要另一边可以传递过来,那么它也可以传递出去,所以可知这种段有没有都无所谓。
  3. 其余的所有区间都可以归为一类,只需要判断相应的端点是否有雾即可。

这里感觉比其它题解少分了一类,简化了很多。

分完类,就有了一些很好的做法了。

我们使用两个 set

一个维护连续段,一个维护第一类和第三类来查端点。

再使用线段树或树状数组维护区间已知点数量。

我采用了线段树。

代码也很好写。

Code#

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;
}

作者:JiaY19

出处:https://www.cnblogs.com/JiaY19/p/17869723.html

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

posted @   JiaY19  阅读(59)  评论(0编辑  收藏  举报
编辑推荐:
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)
more_horiz
keyboard_arrow_up dark_mode palette
选择主题
menu
点击右上角即可分享
微信分享提示