P3901 数列找不同 题解?

P3901 数列找不同

cnblogs

前置知识:

  • bitset 基础用法。

  • 线段树。

Solution:

题目大意这里不再赘述。

提供一个支持强制在线和带修的 bitset 做法。要求值域为 \([1,n]\)

\(N=n+1\)

我们对原序列建线段树,在线段树每个节点上开一个 \(N\) 位 bitset 记录在这个节点所管辖的区间,bitset 的第 \(i\) 位表示 权值为 \(i\) 的数在这个区间出现次数 个 \(1\) 异或的结果。

这样虽然维护的不是哪些权值出现过,但是我们一定可以判断权值是否互不相同,因为如果出现了相同的数,那么 bitset 内 \(1\) 的数量就一定小于区间长度。

但是空间复杂度爆炸,考虑优化。

我们发现,我们不用建出一整棵线段树,可以设立一个阈值 \(B\) ,当区间长度小于 \(B\) 时就不建 bitset,直接返回。否则建立 bitset,初始化时暴力枚举整个区间即可。由于树高只有 \(\log\) 层,故复杂度正确。

\(S=\log_2{N}-\log_2{B}\)

查询时正常线段树区间查询,统计答案时,若区间有 bitset 则把 \(ans\) 异或上这个 bitset,否则直接暴力枚举整个区间,直接统计答案。复杂度 \(O(\frac{NS}{w} + 2B)\)

修改时正常线段树单点修改,当节点有 bitset 时更改 bitset。复杂度 \(O(\log n)\)

时间复杂度瓶颈在于查询。

本题中取 \(B=512\) 时较快。

时间复杂度约为 \(O(n\times(\frac{NS}{w} + 2B))\),不可省略常数项。

Code:


#include<stdio.h>
#include<bitset>
#include<vector>
// #define int long long

using namespace std;

inline int read()
{
	int x=0,c=getchar_unlocked();
	for(;c>'9'||c<'0';c=getchar_unlocked());
	for(;c>='0'&&c<='9';c=getchar_unlocked())
		x=(x<<1)+(x<<3)+(c^48);
	return x;
}

const int N=2e5+5;
int n,q,t;
int a[N];
const int B=512;
bitset<100064> bit[100064/B*5+5],ans;

void build(int l,int r,int p)
{//正常线段树建树
	if(r-l+1<B) return;//区间长度小于 B 直接返回
	int mid=(l+r)>>1,lp=(p<<1),rp=(p<<1)|1;
	build(l,mid,lp);
	build(mid+1,r,rp);

    //否则建立 bitset 
	for(register int i=l;i<=r;i++)// 暴力枚举整个区间
	bit[p][a[i]]=bit[p][a[i]]^1; // 初始化
}

inline void change(int l,int r,int x,int col,int p)
{//正常线段树单点修改
	if(r-l+1<B) return;
	int mid=(l+r)>>1,lp=(p<<1),rp=(p<<1)|1;
	if(x<=mid) change(l,mid,x,col,lp);
	else change(mid+1,r,x,col,rp);

    // 有 bitset 需要修改 bitset
    bit[p][a[x]]=bit[p][a[x]]^1;
    bit[p][col]=bit[p][col]^1;
}

inline void query(int l,int r,int sl,int sr,int p)
{//正常线段树区间查询
	if(sl<=l&&r<=sr)
	{
		if(r-l+1<B)
		{
			for(int i=l;i<=r;i++)//区间长度 < B 直接暴力遍历整个区间统计答案
            ans[a[i]]=ans[a[i]]^1;
		}
		else ans^=bit[p];//有 bitset 直接异或
		return;
	}
	int mid=(l+r)>>1,lp=(p<<1),rp=(p<<1)|1;
	if(sl<=mid) query(l,mid,sl,sr,lp);
	if(sr>mid) query(mid+1,r,sl,sr,rp);
}

inline bool query(int ql,int qr)
{
	if(ql==qr) return 1;

	ans.reset();// 清空 ans
	query(1,n,ql,qr,1);

	return ans.count()==qr-ql+1;
    // count() 返回 bitset 里 1 的个数
    //这样虽然维护的不是哪些权值出现过,但是我们一定可以判断权值是否互不相同,
    //因为如果出现了相同的数,那么 bitset 内 1 的数量就一定小于区间长度。
}

int main()
{
	n=read();
	q=read();
    for(int i=1;i<=n;i++) a[i]=read();

	build(1,n,1);

	for(register int ask=1;ask<=q;ask++)
	{
        /*
        - 注释掉的部分可以支持单点修改
        - 强制在线只需记一个 lastans 即可
        */
		// int op=read();
		// if(op==1)
		// {
		// 	int i=read(),nwc=read();
		// 	change(1,n,i,nwc,1);
		// 	change(1,n,i,nwc,1);
		// 	a[i]=nwc;
		// }
		// else
		// {
		// 	int l=read(),r=read();
		// 	bool as=query(l,r);
		// 	if(as) puts("Yes");
		// 	else puts("No");
		// }
        
        int l=read(),r=read();
		bool as=query(l,r);
		if(as) puts("Yes");
		else puts("No");
	}
	return 0;
}
posted @ 2025-08-02 14:44  Wy_x  阅读(18)  评论(0)    收藏  举报