题目
前几篇文章中出现了“靠”等字眼,一次偶然的机会百度到了一篇转载自我的文章,转载者将问中的包含“情绪”的语句全部删掉了,我这才注意到,最近我变得很容易情绪不好!!我错了,我想起了高中一老师说的“心静如水,激情似火”!看来我需要修炼的东西还很多啊……
题目大意:给定10^5的一个数组,(10^5次)查询给定区间中出现次数和这个数的值相同的数的个数
例如:
7 2 3 1 2 2 3 3 7 1 7 3 47个数2个询问1-7中1出现1次、2出现2次、3出现3次,所以答案为3。3-4中只有2出现2次,所以答案为1
代码写的有点挫,因为调试了好久。首先给定数组后,可以成为答案的数字不会超过500个(1+2*2+3*3+……500*500 > 10^5)
其次,离线处理也是比较常见的!由于x出现恰好是x次,所以需要将向左第x个标记为1,向左第x+1个就要标记为-1,其余标记为0,这样就可以有效的查询区间中恰好出现x次的数字个数(区间求和)
/** 离线线段树问题 */ #include <iostream> #include <stdio.h> #include <string.h> #include <algorithm> #include <map> #include <queue> using namespace std; #define N 100010 int ai[N],_ai[N],re[N]; struct note { int l,r,index; }query[N]; bool q_cmp(const note a,const note b) { return a.r < b.r ||(a.r == b.r && a.l < b.l); } int num[N]; ///线段树 #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 #define fmid int m = (l+r)>>1; int sum[N*4]; void init() { memset(sum,0,sizeof(sum)); } void push_up(int rt) { sum[rt] = sum[rt<<1] + sum[rt<<1|1]; } int f_query(int L,int R,int l,int r,int rt) { if(L <= l && r <= R) { return sum[rt]; } fmid; int re = 0; if(L <= m) re += f_query(L,R,lson); if(m < R) re += f_query(L,R,rson); return re; } void update(int pos,int val,int l,int r,int rt) { if(pos == 0) return; if(l == r) { sum[rt] = val; return; } fmid; if(pos <= m) update(pos,val,lson); else update(pos,val,rson); push_up(rt); } ///PPPPPPPPPPPPPPPPPPPPP void _update(int pos,int val,int l,int r,int rt) { //cout << pos << " " << val << "\n"; update(pos,val,l,r,rt); } int main() { int n,m; while(cin >> n >> m) { for(int i = 1;i <= n;i++) scanf("%d",&ai[i]); for(int i = 0;i < m;i++) scanf("%d%d",&query[i].l,&query[i].r); for(int i = 0;i < m;i++) query[i].index = i; sort(query,query+m,q_cmp); for(int i = 1;i <= n;i++) _ai[i] = ai[i]; sort(_ai+1,_ai+n+1); memset(num,0,sizeof(num)); num[1] = 1; int kk = 1; for(int i = 2;i <= n;i++) { if(_ai[i] == _ai[i-1]) num[kk]++; else { _ai[++kk] = _ai[i]; num[kk] = 1; } } int k = 1; for(int i = 1;i <= kk;i++) { if(num[i] >= _ai[i]) _ai[k++] = _ai[i]; } map <int ,int> mymap; kk = 1; for(int i = 1;i < k;i++) { mymap[ _ai[i] ] = kk++; } //for(int i = 1;i < k;i++) cout << _ai[i] << " ";cout << kk << " ppp\n"; kk = 0; init(); queue < int > my_queue[500]; int t_pos[500]; memset(t_pos,0,sizeof(t_pos)); for(int i = 1;i <= n;i++) { int t = mymap[ ai[i] ]; if(t != 0) { my_queue[t].push(i); int size = my_queue[t].size() ; if(size == ai[i]) _update(my_queue[t].front(),1,1,n,1);//第一出出现ai[i]次 if(size == ai[i]+1) { _update(t_pos[t],0,1,n,1);//注意将-1左侧的改为0 t_pos[t] = my_queue[t].front();//记录标记为-1的点,已备以后改为0 _update(my_queue[t].front(),-1,1,n,1);//修改为-1 my_queue[t].pop(); _update(my_queue[t].front(),1,1,n,1);//将左侧底ai[i]个ao[i]标记为1 } } while(kk < m && query[kk].r == i) { re[query[kk].index] = f_query(query[kk].l,query[kk].r,1,n,1);//查询该区间的值得情况 kk++; } } for(int i = 0;i < m;i++) cout << re[i] << "\n"; } return 0; }