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
Input 5 1 1 2 1 3 3 1 5 2 4 3 5 Output 3 2 3
题意:求区间内元素的种数
思路:如果有一个询问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,r int 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 6008000 int 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; }