SPOJ3267--D-query(离线线段树||在线主席树)

来源:互联网 发布:求好听的淘宝店铺名字 编辑:程序博客网 时间:2024/05/16 11:49

Given a sequence of n numbers a1, a2, ..., an and a number of d-queries. A d-query is a pair (i, j) (1 ≤ i ≤ j ≤ n). For each d-query (i, j), you have to return the number of distinct elements in the subsequence ai, ai+1, ..., aj.

Input

  • Line 1: n (1 ≤ n ≤ 30000).
  • Line 2: n numbers a1, a2, ..., an (1 ≤ ai ≤ 106).
  • Line 3: q (1 ≤ q ≤ 200000), the number of d-queries.
  • In the next q lines, each line contains 2 numbers i, j representing a d-query (1 ≤ i ≤ j ≤ n).

Output

  • For each d-query (i, j), print the number of distinct elements in the subsequence ai, ai+1, ..., aj in a single line.

Example

Input51 1 2 1 331 52 43 5Output323 
题意:求区间内元素的种数
思路:如果有一个询问u,v。则ans[v].push_back(u)。
      然后就从左往右扫描,先更新数,如果没出现过很好处理。如果出现过,删除之前的出现记录,然后出现点更新为这个地方。然后如果这个点有      询问,就query一下。
      线段树维护区间内的数种数。
#include <iostream>#include <cstdio>#include <vector>#include <cstring>#include <algorithm>using namespace std;#define maxn 30800#define maxm 1008000#define lson id<<1,l,mid#define rson id<<1|1,mid+1,rint pos[maxm],key[maxn],Ans[208000];struct ST{int l,r,sum;}st[maxn<<2];void PushUp(int id){st[id].sum = st[id<<1].sum + st[id<<1|1].sum;}void buildtree(int id,int l,int r){st[id].l = l,st[id].r = r;st[id].sum = 0;if(l == r)return;int mid = (l+r) >> 1;buildtree(lson);buildtree(rson);}void Update(int id,int p){if(st[id].l == p && st[id].r == p){st[id].sum ^= 1;return;}if(st[id<<1].r >= p)Update(id<<1,p);else Update(id<<1|1,p);PushUp(id);}int Query(int id,int l,int r){if(st[id].l == l && st[id].r == r)return st[id].sum;if(st[id<<1].r >= r)return Query(id<<1,l,r);else if(st[id<<1|1].l <= l)return Query(id<<1|1,l,r);return Query(id<<1,l,st[id<<1].r) + Query(id<<1|1,st[id<<1|1].l,r);}vector <int> ans[maxn];vector <int> num[maxn];int main(){//freopen("in.txt","r",stdin);int n;while(scanf("%d",&n)!=EOF){memset(pos,0,sizeof(pos));for(int i = 1;i <= n;i++)scanf("%d",&key[i]);for(int i = 1;i <= n;i++){num[i].clear();ans[i].clear();}int m;scanf("%d",&m);for(int i = 1;i <= m;i++){int u,v;scanf("%d%d",&u,&v);ans[v].push_back(u);num[v].push_back(i);}buildtree(1,1,n);for(int i = 1;i <= n;i++){if(pos[key[i]])Update(1,pos[key[i]]);Update(1,i);pos[key[i]] = i;for(int j = 0;j < ans[i].size();j++){int u = ans[i][j];Ans[num[i][j]] = Query(1,u,i);}}for(int i = 1;i <= m;i++)printf("%d\n",Ans[i]);}return 0;}

此题也可在线做,用主席树。
给数组的每一个后缀都建立一棵线段树,然后从后往前。类似于上面线段树的做法,线段树中一个数之前出现过,从左往右扫的过程中不断将这个数出现的位置往右更新。。而主席树我从右往左扫,不断将数出现的位置往左更新。
#include <iostream>#include <cstdio>#include <cstring>using namespace std;#define maxn 30080#define maxm 6008000int T[maxn],key[maxn];int Pos[1008000];int c[maxm],lson[maxm],rson[maxm];int tot,n;int build(int l,int r){int root = tot++;c[root] = 0;if(l != r){int mid = (l+r) >> 1;lson[root] = build(l,mid);rson[root] = build(mid+1,r);}return root;}int update(int root,int pos,int val){int newnode = tot++,tmp = newnode;c[newnode] = c[root] + val;int l = 1,r = n;while(l < r){int mid = (l+r) >> 1;if(pos <= mid){lson[newnode] = tot++;rson[newnode] = rson[root];newnode = lson[newnode];root = lson[root];r = mid;}else {rson[newnode] = tot++;lson[newnode] = lson[root];newnode = rson[newnode];root = rson[root];l = mid + 1;}c[newnode] = c[root] + val;}return tmp;}int query(int root,int pos){int ret = 0;int l = 1,r = n;while(pos < r){int mid = (l+r) >> 1;if(pos <= mid){r = mid;root = lson[root];}else {ret += c[lson[root]];root = rson[root];l = mid + 1;}}return ret + c[root];}int main(){//freopen("in.txt","r",stdin);while(scanf("%d",&n)!=EOF){memset(Pos,0,sizeof(Pos));tot = 0;for(int i = 1;i <= n;i++)scanf("%d",&key[i]);T[n+1] = build(1,n);for(int i = n;i;i--){if(Pos[key[i]])//如果这个数曾经出现过{int t = update(T[i+1],Pos[key[i]],-1);Pos[key[i]] = i;T[i] = update(t,i,1);}else {Pos[key[i]] = i;T[i] = update(T[i+1],i,1);}}int m;scanf("%d",&m);while(m--){int l,r;scanf("%d%d",&l,&r);printf("%d\n",query(T[l],r));}}return 0;}


0 0
原创粉丝点击