SPOJ D-query && HDU 3333 Turing Tree (线段树 && 区间不相同数个数or和 && 离线处理)
题意 : 给出一段n个数的序列,接下来给出m个询问,询问的内容SPOJ是(L, R)这个区间内不同的数的个数,HDU是不同数的和
分析 :
一个经典的问题,思路是将所有问询区间存起来,然后按右端点排序
最后从左到右将原区间扫一遍,扫的过程当中不断将重复出现的数字右移
也就是如果一个数字重复出现,那么我们记录最右边的那个为有效的,其他都视为不存在
这样一旦遇到一个问询区间的右端点就线段树查询即可。
SPOJ:
#include<bits/stdc++.h> using namespace std; #define lson l , m , rt << 1 #define rson m + 1 , r , rt << 1 | 1 const int maxn = 300005; long long sumv[maxn<<2]; void PushUP(int rt) { sumv[rt] = sumv[rt<<1] + sumv[rt<<1|1]; } void update(int p,int sc,int l,int r,int rt) { if (l == r) { sumv[rt] += sc; return ; } int m = l + ((r - l)>>1); if (p <= m) update(p , sc , lson); else update(p , sc , rson); PushUP(rt); } long long query(int L,int R,int l,int r,int rt) { if (L <= l && r <= R) { return sumv[rt]; } int m = l + ((r - l)>>1); long long ret = 0; if (L <= m) ret += query(L , R , lson); if (R > m) ret += query(L , R , rson); return ret; } struct Interval{ int l, r, id; bool operator < (const Interval& other) const{ return this->r < other.r; } }; Interval sec[200005]; map<int, int> mp; int arr[maxn]; int pre[maxn]; long long ans[maxn]; int main(void) { int n, cnt = 0, m; scanf("%d", &n); for(int i=1; i<=n; i++){ scanf("%d", &arr[i]); if(!mp.count(arr[i])) mp[arr[i]] = cnt++;//区间上的每一个数都有一个独特的编号,使用map来进行映射,目的是方便判断此数是否已经重复出现过 } scanf("%d", &m); for(int i=1; i<=m; i++){ scanf("%d %d", &sec[i].l, &sec[i].r); sec[i].id = i;//因为后续有排序操作,而我们又要对问询答案顺序给出,所以记录给出的顺序,方便输出答案 } sort(sec+1, sec+1+m);//按照右端点排序 bool vis[maxn]; memset(vis, false, sizeof(vis)); for(int i=1, j=1; j<=m && i<=n; i++){ int tmp = mp[arr[i]];//拿出这个数的编号 if(vis[tmp]){//如果这个数在前面已经访问过 update(pre[tmp], -1, 1, n, 1);//将之前的位置信息抹去 update(i, 1, 1, n, 1);//当前的位置才是有效的,所以给当前位置+1 pre[tmp] = i;//更新pre数组方便下次操作 }else{ vis[tmp] = true; update(i, 1, 1, n, 1); pre[tmp] = i; } while(sec[j].r == i){ ans[sec[j].id] = query(sec[j].l, sec[j].r, 1, n, 1); j++; } } for(int i=1; i<=m; i++){ printf("%I64d\n", ans[i]); } return 0; }
HDU:
#include<bits/stdc++.h> using namespace std; #define lson l , m , rt << 1 #define rson m + 1 , r , rt << 1 | 1 const int maxn = 30010; long long sumv[maxn<<2]; void PushUP(int rt) { sumv[rt] = sumv[rt<<1] + sumv[rt<<1|1]; } void update(int p,long long sc,int l,int r,int rt) { if (l == r) { sumv[rt] += sc; return ; } int m = l + ((r - l)>>1); if (p <= m) update(p , sc , lson); else update(p , sc , rson); PushUP(rt); } long long query(int L,int R,int l,int r,int rt) { if (L <= l && r <= R) { return sumv[rt]; } int m = l + ((r - l)>>1); long long ret = 0; if (L <= m) ret += query(L , R , lson); if (R > m) ret += query(L , R , rson); return ret; } struct interval{ int L, R, id; bool operator < (const interval & other) const{ return this->R < other.R; } }; interval sec[maxn<<2]; bool vis[maxn]; long long ans[maxn<<2]; int per[maxn]; long long arr[maxn]; map<long long, int> mp; int main(void) { int nCase; scanf("%d", &nCase); while(nCase--){ int n; mp.clear(); int cnt = 0; memset(sumv, 0, sizeof(sumv)); scanf("%d", &n); for(int i=1; i<=n; i++){ scanf("%I64d", &arr[i]); if(!mp.count(arr[i])) mp[arr[i]] = cnt++; } int m; scanf("%d", &m); for(int i=1; i<=m; i++){ scanf("%d %d", &sec[i].L, &sec[i].R); sec[i].id = i; } sort(sec+1, sec+1+m); memset(vis, false, sizeof(vis)); for(int i=1, j=1; j<=m && i<=n; i++){ int num = mp[arr[i]]; if(vis[num]){ update(per[num],-arr[i],1,n,1); update(i, arr[i], 1, n, 1); per[num] = i; }else{ vis[num] = true; update(i, arr[i], 1, n, 1); per[num] = i; } while(i==sec[j].R){ ans[sec[j].id] = query(sec[j].L, sec[j].R, 1, n, 1); j++; } } for(int i=1; i<=m; i++){ printf("%I64d\n", ans[i]); } } }
update in 2018-06-08
学了主席树、以上的离线算法都可以变成在线算法了、戳 主席树