4939: [Ynoi2016]掉進兔子洞
Description
您正在打galgame,然後突然發現您今天太頹了,於是想寫個數據結構題練練手:
一個長爲 n 的序列 a。
有 m 個詢問,每次詢問三個區間,把三個區間中同時出現的數一個一個刪掉,問最後三個區間剩下的數的個數和,詢問獨立。
注意這裏刪掉指的是一個一個刪,不是把等於這個值的數直接刪完,
比如三個區間是 [1,2,2,3,3,3,3] , [1,2,2,3,3,3,3] 與 [1,1,2,3,3],就一起扔掉了 1 個 1,1 個 2,2 個 3。
Input
第一行兩個數表示 n , m。
第二行 n個數表示 a[i]。
之後 m 行,每行 6 個數 l1 , r1 , l2, r2 , l3 , r3 表示這三個區間。
Output
對於每個詢問,輸出一個數表示答案。
Sample Input
5 2
1 2 2 3 3
1 2 2 3 3 4
1 5 1 5 1 5
Sample Output
3
0
HINT
n , m <= 100000 , 1 <= a[i] <= 1000000000
顯然是要求 ,
考慮求後面的部分。首先肯定要離散化。
可以將每個詢問拆成三個,分別處理出每個區間的 數組。而 數組可以用莫隊來得到。
現在考慮如何較快的將三個 取最小後求和。這個可以用 來做到。
考慮離散化的時候,比如樣例1 2 2 3 3,離散化後得到1 2 2 4 4,那麼在 中,就可以用第3個bit位來表示第二個出現的2,用第2個bit位來表示第一個出現的2。然後只需要把三個bitset與起來就得到答案了。
代碼:
#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<bitset>
#include<cmath>
#define N 100005
using namespace std;
const int T=25000;
bitset<100000>F[25001],f;
int n,A[N],B[N],cnt[N],id[N],S,tot,ans[N],Ans[N],TOT;
bool mark[N];
struct node{int l,r,id;}K[N];
bool cmp(node a,node b)
{
if(id[a.l]==id[b.l])return a.r<b.r;
return id[a.l]<id[b.l];
}
void UD(int k,int ty)
{
k=A[k];cnt[k]+=ty;
if(ty==1)f[k+cnt[k]-2]=1;
else f[k+cnt[k]-1]=0;
}
void Solve(int m)
{
int i,j,k,L,R,l1,l2,l3,r1,r2,r3;
memset(cnt,0,sizeof(cnt));
memset(mark,0,sizeof(mark));
f.reset();tot=0;
for(i=1;i<=m;i++)
{
scanf("%d%d%d%d%d%d",&l1,&r1,&l2,&r2,&l3,&r3);
K[++tot]=(node){l1,r1,i};
K[++tot]=(node){l2,r2,i};
K[++tot]=(node){l3,r3,i};
ans[i]=r3+r2+r1-l3-l2-l1+3;
}
sort(K+1,K+tot+1,cmp);
L=1;R=0;
for(i=1;i<=tot;i++)
{
while(R<K[i].r)UD(++R,1);
while(R>K[i].r)UD(R--,-1);
while(L<K[i].l)UD(L++,-1);
while(L>K[i].l)UD(--L,1);
if(mark[K[i].id])F[K[i].id]&=f;
else F[K[i].id]=f,mark[K[i].id]=1;
}
for(i=1;i<=m;i++)
{
k=F[i].count();
printf("%d\n",ans[i]-3*k);
}
}
int main()
{
int i,j,k,m,x,y,l,r;
scanf("%d%d",&n,&m);S=sqrt(n);
for(i=j=1;i<=n;i++)scanf("%d",&A[i]),B[i]=A[i],id[i]=i%S?j:j++;
sort(B+1,B+n+1);
for(i=1;i<=n;i++)A[i]=lower_bound(B+1,B+n+1,A[i])-B;
while(m)
{
if(m<=T)Solve(m),m=0;
else Solve(T),m-=T;
}
}