P3901 数列找不同 题解?
前置知识:
-
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;
}
以下是博客签名,正文无关
本文来自博客园,作者:Wy_x,转载请在文首注明原文链接:https://www.cnblogs.com/Wy-x/articles/19018805
版权声明:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议(CC-BY-NC-SA 4.0 协议)进行许可。

浙公网安备 33010602011771号