SDOI2009 HH的项链
这道题原来是可以用莫队暴力过掉的……但是数据加强之后500000万的范围使得nsqrtn的算法无法通过,那我们只能用log的方法,也就是树状数组。
本题要求我们统计的是贝壳的个数,我们唯一的困难在于如何判断重复元素。考虑这样一个事情,对于一些右端点相同的区间,我们在统计这些区间之内的情况的时候,重复的元素我们只关心它出现在最右边的一个。
举例子,比如区间1,2,3,2,4 对于这个长度为5的区间,我们对于每个诸如[l,5]的询问,第一个2完全不需要被考虑,因为它已经被第二个2完全覆盖了。(左端点靠前的,2可以被后面的计算,靠后的就完全没前面事了)
所以我们可以把所有的区间按右端点排序,之后离线处理。对于每个右端点,把在其之前的都在树状数组中加上,而已经出现过的元素,就在原来的位置先删除,之后再在当前位置添加。统计的时候直接计算两端和相减即可。
听起来是不是很简单……?
看一下代码。
#include<cstdio> #include<algorithm> #include<cstring> #include<cmath> #include<queue> #define rep(i,a,n) for(int i = a;i <= n;i++) #define per(i,n,a) for(int i = n;i >= a;i--) #define enter putchar('\n') using namespace std; const int M = 1005; const int N = 1000005; const int INF = 1e9; double eps = 1e-7; typedef long long ll; int read() { int ans = 0,op = 1; char ch = getchar(); while(ch < '0' || ch > '9') { if(ch == '-') op = -1; ch = getchar(); } while(ch >= '0' && ch <= '9') { ans *= 10; ans += ch - '0'; ch = getchar(); } return ans * op; } struct ask { int l,r,pos; bool operator < (const ask &g) const { if(r == g.r) return l < g.l; return r < g.r; } }a[N]; struct answer { int val,p; bool operator < (const answer &g) const { return p < g.p; } }ans[N]; int n,m,shell[N],cur = 1,c[N],loc[N]; bool vis[N]; int lowbit(int x) { return x & (-x); } void add(int x,int v) { while(x <= n) { c[x] += v; x += lowbit(x); } } int query(int x) { int sum = 0; while(x) { sum += c[x]; x -= lowbit(x); } return sum; } int main() { n = read(); rep(i,1,n) shell[i] = read(); m = read(); rep(i,1,m) a[i].l = read(),a[i].r = read(),a[i].pos = i; sort(a+1,a+1+m); rep(i,1,m) { while(cur <= a[i].r)//所有在前面的都要统计 { if(!vis[shell[cur]]) add(cur,1),loc[shell[cur]] = cur,vis[shell[cur]] = 1;//这个元素没出现过 else add(loc[shell[cur]],-1),add(cur,1),loc[shell[cur]] = cur;//元素出现过 cur++; } ans[i].val = query(a[i].r) - query(a[i].l-1),ans[i].p = a[i].pos;//计算出现次数 } sort(ans+1,ans+1+m);//注意要重新按照出现的次序排序 rep(i,1,m) printf("%d\n",ans[i].val); return 0; }
当你意识到,每个上一秒都成为永恒。