• 题意
    • 如题
  • 题解
    • 树状数组可以用来做这样一件事,即保存前缀和,并可以单点修改它(也可以用差分区间修改)
    • 一般区间的操作不用树状数组,可是当区间操作有这样两个性质时,可以考虑使用树状数组
      • 前面区间的前缀和的计算值在所有区间都相同,这样我们在查询前缀和时就不会因为每个区间计算前缀和的值不同(当我们在ask(r)-ask(l-1)时,其实是默认了每次的区间前缀和(1~l-1)相同的)
      • 区间修改转化为单调修改时只需要改变n个点的状态(也可以说是每个点只被修改一次)
    • 比如求回文半径相互覆盖区间个数时,计算(1~l-1)的前缀和的值相同,而所有的左端点对应的右端点只有n个
    • 回到这一题,我们可以发现,区间的查询按左端点排序后,是可以满足单调的,而查询时也只要修改now到q[i].l的区间,但是我们发现,不同查询区间的(1~l-1)前缀计算是不同的,我们要消除它的影响
    • 那么我们考虑,树状数组里储存什么值,可以使得每个1~l-1的前缀的影响消除,并使得不需要重复修改(只修改n次)
    • 我们让树状数组的第i个位置代表位置i是否是查询区间的左边(1~l-1)第一个出现的数,那么每次查询时,只要ask(r)-ask(l-1)就行了(所以树状数组上的值只会是0或1)
    • 我们先在树状数组中add(i,1),其中i是每个数第一次出现的位置
    • 然后记录下每个位置i的下一个与a[i]相同值的位置值
    • 最后按照询问区间的左端点排序,设置now为上一次左端点位置,那么每次询问前遍历now~l-1位置,将其中每个位置的值下一次出现的位置add(nxt[now],1)即可
    • 最后常规查询
  • 代码
    • #include<algorithm>
      #include <iostream>
      #include  <fstream>
      #include  <cstdlib>
      #include  <cstring>
      #include  <cassert>
      #include   <cstdio>
      #include   <vector>
      #include   <string>
      #include    <cmath>
      #include    <queue>
      #include    <stack>
      #include      <set>
      #include      <map>
      using namespace std;
      #define P(a,b,c) make_pair(a,make_pair(b,c))
      #define rep(i,a,n) for (int i=a;i<=n;i++)
      #define per(i,a,n) for (int i=n;i>=a;i--)
      #define CLR(vis) memset(vis,0,sizeof(vis))
      #define MST(vis,pos) memset(vis,pos,sizeof(vis))
      #define pb push_back
      //#define mp make_pair
      #define all(x) (x).begin(),(x).end()
      #define fi first
      #define se second
      #define SZ(x) ((int)(x).size())
      typedef pair<int,pair<int,int> >pii;
      typedef long long ll;
      typedef unsigned long long ull;
      const ll mod = 1000000007;
      const int INF = 0x3f3f3f3f;
      ll gcd(ll a, ll b) {
      	return b ? gcd(b, a%b) : a;
      }
      template<class T>inline void gmax(T &A, T B) {
      	(A<B)&&(A=B);//if(B>A)A=B;
      }
      template<class T>inline void gmin(T &A, T B) {
      	(A>B)&&(A=B);//if(B<A)A=B;
      }
      template <class T>
      inline bool scan(T &ret) {
      	char c;
      	int sgn;
      	if(c=getchar(),c==EOF) return 0; //EOF
      	while(c!='-'&&(c<'0'||c>'9')) c=getchar();
      	sgn=(c=='-')?-1:1;
      	ret=(c=='-')?0:(c-'0');
      	while(c=getchar(),c>='0'&&c<='9') ret=ret*10+(c-'0');
      	ret*=sgn;
      	return 1;
      }
      inline void outt(ll x) {
      	if(x>9) outt(x/10);
      	putchar(x%10+'0');
      }
      
      const int N = 1e6+10;
      const int maxn = 1e6+10;
      
      int nxt[maxn],n;
      
      int c[maxn];
      void add(int x,int y){
      	for( ; x<=n ; x+=x&-x)c[x] += y;
      }
      int ask(int x){
      	int ans = 0;
      	for(; x;x-=x&-x)ans+=c[x];
      	return ans;
      }
      
      void init(){
      	CLR(nxt);
      	CLR(c);
      }
      int ans[maxn];
      struct ss{
      	int l,r,id;
      	bool operator <(const ss &a) const{
      		return l<a.l;
      	}
      }q[maxn];
      
      int vis[maxn];
      int a[maxn];
      int main(){
      	scanf("%d", &n);
      	init();
      	CLR(vis);
      	rep(i,1,n){
      		scanf("%d", &a[i]);
      		if(!vis[a[i]]){
      			vis[a[i]] = 1;
      			add(i,1);
      		}
      		vis[a[i]] = i;
      	}
      	CLR(vis);
      	per(i,1,n){
      		if(!vis[a[i]])nxt[i] = n+1;
      		else{
      			nxt[i] = vis[a[i]];
      		}
      		vis[a[i]] = i;
      	}
      	int m;
      	scanf("%d", &m);
      	rep(i,1,m)
      		scanf("%d%d", &q[i].l, &q[i].r),q[i].id=i;
      	sort(q+1,q+m+1);
      	int l=1;
      	rep(i,1,m){
      		while(l<q[i].l)add(nxt[l],1),++l;
      		ans[q[i].id] = ask(q[i].r)-ask(q[i].l-1);
      	}
      	rep(i,1,m){
      		printf("%d\n", ans[i]);
      	}
      	return 0;	
      } 

发表评论

邮箱地址不会被公开。 必填项已用*标注