luogu P3792 由乃与大母神原型和偶像崇拜

博客围绕一道题目展开,题目是对n个数进行若干组询问,判断[l,r]经排序能否形成值域严格上升序列。最初考虑分块思想但时间复杂度高,后用线段树维护,又结合哈希思想,开线段树维护区间最大值、最小值、平方和,不过存在反例,求平方和时会爆long long,按题解改用循环求解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

背景:

无…

题目传送门:

https://www.luogu.org/problemnew/show/P3792

题意:

n n n个数,若干组询问,每一次询问 [ l , r ] [l,r] [l,r]中是否经过排序可以形成值域严格上升的序列。

思路:

显然用分块的思想(记录上一个和当前相等的数是否在当前块外)很容易实现,参见HH的项链(有些类似,好像不是一回事,但我找不到之前做的这样的题了) 。
但时间复杂度却承受不了。

于是想到了线段树来维护,那么时间复杂度降为了 Θ ( n l o g 2 n ) \Theta(nlog^2n) Θ(nlog2n)

还是不对劲。
想不到了。
于是翻了题解。暴怒。 原来用 h a s h hash hash的思想,开一棵线段树维护区间最大值,最小值,平方和(当然用立方和更好,但值域太大,我放弃了)。
那么,对于一段区间 [ l , r ] [l,r] [l,r],记最大值为 M a x Max Max,最小值为 M i n Min Min,平方和为 S u m Sum Sum,必然存在 r − l = M a x − M i n r-l=Max-Min rl=MaxMin,且 ∑ i = m i n M a x i 2 = S u m \sum^{Max}_{i=min}i^2=Sum i=minMaxi2=Sum
但这不能保证 100 % 100\% 100%正确。
反例如下:

example:
9 1
1 2 3 4 5 2 9 8 9
2 1 9

correct answer:yuanxing
wrong answer:damushen

因此用三次方能更好的避免冲突。
但是值域为 1 0 9 10^9 109,很难接受,我还是放弃了。
有一点,在求 ∑ i = m i n M a x i 2 \sum^{Max}_{i=min}i^2 i=minMaxi2的时候会爆 l o n g   l o n g long\ long long long,因此无法用前缀和+数学推导来求,按照题解的说法,改用循环求解,不会超时。

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long
#define INF (int)(1e9)+1
using namespace std;
	struct node{int l,r,lc,rc,mi,ma;LL sum;} tr[1000010];
	int n,m,len=0;
void build(int l,int r)
{
	int now=++len,mid=(l+r)>>1;
	tr[now]=(node){l,r,-1,-1,INF,-INF,0};
	if(l<r)
	{
		tr[now].lc=len+1; build(l,mid);
		tr[now].rc=len+1; build(mid+1,r);
	}
}
void change(int now,int l,int r,int k)
{
	if(l==tr[now].l&&r==tr[now].r)
	{
		tr[now].ma=tr[now].mi=k;
		tr[now].sum=(LL)k*k;
		return;
	}
	int lc=tr[now].lc,rc=tr[now].rc,mid=(tr[now].l+tr[now].r)>>1;
	if(r<=mid) change(lc,l,r,k);
	else if(l>mid) change(rc,l,r,k);
	else change(lc,l,mid,k),change(rc,mid+1,r,k);
	tr[now].ma=max(tr[lc].ma,tr[rc].ma);
	tr[now].mi=min(tr[lc].mi,tr[rc].mi);
	tr[now].sum=tr[lc].sum+tr[rc].sum;
}
node find(int now,int l,int r)
{
	if(l==tr[now].l&&r==tr[now].r) return tr[now];
	int lc=tr[now].lc,rc=tr[now].rc,mid=(tr[now].l+tr[now].r)>>1;
	if(r<=mid) return find(lc,l,r);
	else if(l>mid) return find(rc,l,r);
	else
	{
		node tmp,tmp1=find(lc,l,mid),tmp2=find(rc,mid+1,r);
		tmp.ma=max(tmp1.ma,tmp2.ma);
		tmp.mi=min(tmp1.mi,tmp2.mi);
		tmp.sum=tmp1.sum+tmp2.sum;
		return tmp;
	}
}
LL calc(int x,int y)
{
	LL sum=0;
	for(int i=x;i<=y;i++)
		sum+=(LL)i*i;
	return sum;
}
bool work(int x,int y)
{
	node ans=find(1,x,y);
	int t1=ans.mi,t2=ans.ma;
	if(y-x!=t2-t1) return false;
	return calc(t1,t2)==ans.sum;
}
int main()
{
	int t,x,y;
	scanf("%d %d",&n,&m);
	build(1,n);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&x);
		change(1,i,i,x);
	}
	for(int i=1;i<=m;i++)
	{
		scanf("%d %d %d",&t,&x,&y);
		switch(t)
		{
			case 1:change(1,x,x,y);break;
			case 2:printf(work(x,y)?"damushen\n":"yuanxing\n");break;
		}
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值