给你一个长度为 \(n(n\le 10^6)\) 的序列,其中每个元素都在 \([1,n]\) 范围中。
询问有多少个区间满足区间中的元素都恰好出现 \(2\) 次。
\(\texttt{solution}~\texttt{1:}\) 哈希
我们考虑如果元素个数 \(\le 64\) 时我们怎么做:
给每个元素分配一个二进制位,记录到当前为止的异或和,同时用 map
记录同为这个异或和的位置数量(当然要保证出现位置在这个元素上上次出现以后),并累加答案。
但是如果元素个数过多怎么办?答案是哈希!!!
我们给每个元素随机附上一个值,向上面一样添加和求出答案。
事实证明在随机赋值是正确率是非常高的。
$\texttt{code}$#define Maxn 1000005
typedef unsigned long long ll;
int n,pos;
ll ans;
int a[Maxn],llast[Maxn],last[Maxn];
ll ha[Maxn],pre[Maxn];
unordered_map<ll,int> cnt;
int main()
{
srand(time(0));
n=rd();
for(int i=1;i<=n;i++) a[i]=rd(),ha[i]=1llu*rand()*rand()*rand()*rand()*rand()*rand();
cnt[0]++;
for(int i=1;i<=n;i++)
{
while(pos<llast[a[i]]) cnt[pre[pos]]--,pos++;
llast[a[i]]=last[a[i]],last[a[i]]=i;
pre[i]=pre[i-1]^ha[a[i]];
ans+=cnt[pre[i]];
cnt[pre[i]]++;
}
printf("%llu\n",ans);
return 0;
}
\(\texttt{solution}~\texttt{2:}\) 扫描线
我们记 \(Pos(i,1/2/3)\) 表示当前右端点以左,离数字 \(i\) 距离第 \(1,2,3\) 远的位置。
易知当左端点位于 \([1,Pos(3)],[Pos(2)+1,Pos(1)]\) 时答案不合法,所有不合法位置的并集就是所有不合法位置。
可以通过总位置减去不合法位置来求出合法位置,因此变成了一道区间染色问题。
我们通常用扫描线(不下传标记)来维护区间是否被染色,这题也可以这么维护。
$\texttt{code}$#define Maxn 1000005
typedef long long ll;
int n,ans;
int a[Maxn],Last[Maxn][3];
struct TREE{ int laz,sum; }tree[Maxn<<3];
inline void pushup(int p,int nl,int nr)
{
if(tree[p].laz>0) tree[p].sum=nr-nl+1;
else tree[p].sum=tree[p<<1].sum+tree[p<<1|1].sum;
}
void add(int p,int nl,int nr,int l,int r,int k)
{
if(l>r) return;
if(nl>=l && nr<=r) { tree[p].laz+=k,pushup(p,nl,nr); return; }
int mid=(nl+nr)>>1;
if(mid>=l) add(p<<1,nl,mid,l,r,k);
if(mid<r) add(p<<1|1,mid+1,nr,l,r,k);
pushup(p,nl,nr);
}
int main()
{
n=rd();
for(int i=1;i<=n;i++) a[i]=rd();
for(int i=1;i<=n;i++)
{
add(1,1,n,1,Last[a[i]][2],-1);
add(1,1,n,Last[a[i]][1]+1,Last[a[i]][0],-1);
Last[a[i]][2]=Last[a[i]][1];
Last[a[i]][1]=Last[a[i]][0];
Last[a[i]][0]=i;
add(1,1,n,1,Last[a[i]][2],1);
add(1,1,n,Last[a[i]][1]+1,Last[a[i]][0],1);
ans+=i-tree[1].sum;
}
printf("%d\n",ans);
return 0;
}