說明:
$S_{i}$為序列中第$i$個元素
和這題一樣,我們可以考慮對每個$i$儲存上一個$S_{i}$出現的位置,叫它$PRE[i]$好了
如果$S_{i}$是第一個出現,$PRE[i]=0$
你可以用任何方法將$PRE[i]$ $O(N\log N)$預處理出來
我偷懶用了$map$
那麼,要怎麼利用$PRE[i]$來求出區間$[l,r]$ (題中的$[i,j]$) 的答案呢?
我們可以用區間長度減掉重複的次數
重複的次數怎麼算呢?
就是$PRE[l\sim r]$中有幾個$\geq l$啦 (因為上一個$S_{i}$也在區間裡面,所以$S_{i}$不是區間中第一次出現)
可以發現,$PRE[1\sim l-1]$一定都$<l$,因此答案也等於$PRE[1\sim r]$中有幾個$\geq l$
可以用線段樹實現$O(\log N)$單點修改、區間求和
然後對於每個$r$,維護$PRE[1\sim r]$中有幾個位於任意區間內
有兩個方向
1. 離線操作
2. 持久化
我的code使用了持久化
時間複雜度:$O(N\log N+Q\log N)$
code:
#include<cstdio> #include<cassert> #include<map> using namespace std; struct Node { Node *ch[2]; int sum; Node(const int _sum):ch{NULL,NULL},sum(_sum){} }; Node *Build(const int l,const int r) { Node *ans=new Node(0); if(l<r) { const int mid=(l+r)/2; ans->ch[0]=Build(l,mid),ans->ch[1]=Build(mid+1,r); } return ans; } Node *Modify(Node *o,const int l,const int r,const int loc,const int val) { Node *ans=new Node((o->sum)+val); if(l<r) { const int mid=(l+r)/2; if(loc<=mid)ans->ch[0]=Modify(o->ch[0],l,mid,loc,val),ans->ch[1]=o->ch[1]; else ans->ch[0]=o->ch[0],ans->ch[1]=Modify(o->ch[1],mid+1,r,loc,val); } return ans; } int Query(Node *o,const int l,const int r,const int loc) { if(l>=loc)return o->sum; if(r<loc)return 0; const int mid=(l+r)/2; return Query(o->ch[0],l,mid,loc)+Query(o->ch[1],mid+1,r,loc); } int N,S[30001],PRE[30001]; Node *TREE[30001]; int main() { // freopen("in.txt","r",stdin); scanf("%d",&N); for(int i=1;i<=N;i++)scanf("%d",&S[i]); map<int,int>pre; for(int i=1;i<=N;i++) { PRE[i]=pre[S[i]]; pre[S[i]]=i; } TREE[0]=Build(0,N); for(int i=1;i<=N;i++) { TREE[i]=TREE[i-1]; TREE[i]=Modify(TREE[i],0,N,PRE[i],1); } int querycount;scanf("%d",&querycount); for(int i,j;querycount--;) { scanf("%d%d",&i,&j); printf("%d\n",(j-i+1)-Query(TREE[j],0,N,i)); } return 0; }
沒有留言:
張貼留言
歡迎留言或問問題~
若您的留言中包含程式碼,請參考這篇
如果留言不見了請別慌,那是因為被google誤判成垃圾留言,小莫會盡快將其手動還原
注意:只有此網誌的成員可以留言。