【Codeforces 220B】 Little Elephant and Array | 扫描线、树状数组

感谢宇巨抛过来的题

题目大意:

给出一个长度为n的序列,进行m次询问。

每次询问区间 [ l , r ] [l,r] [l,r]内,有多少个数字 x x x刚好出现了 x x x次。

题目思路:

类比维护区间内出现了多少种数字

用扫描线+树状数组的去考虑

枚举右端点 r r r,维护左端点 l l l,设法将 s u m ( l , r ) sum(l,r) sum(l,r)表示为区间内的合法数字
个数

所以以区间 [ 2 , 2 , 2 , 2 ] [2,2,2,2] [2,2,2,2]为例:

1. r = 1 r = 1 r=1,左端点的贡献分别为: [ 0 , 0 , 0 , 0 ] [0,0,0,0] [0,0,0,0];
2. r = 2 r = 2 r=2,左端点的贡献分别为: [ 1 , 0 , 0 , 0 ] [1,0,0,0] [1,0,0,0];
3. r = 3 r = 3 r=3,左端点的贡献分别为: [ − 1 , 1 , 0 , 0 ] [-1,1,0,0] [1,1,0,0];
4. r = 4 r = 4 r=4,左端点的贡献分别为: [ 0 , − 1 , 1 , 0 ] [0,-1,1,0] [0,1,1,0];

所以可以看出规律,只需要维护上述规律即可将贡献维护成前缀和。

Code:

/*** keep hungry and calm CoolGuang!  ***/
//#pragma GCC optimize(3)
#include <bits/stdc++.h>
#include<stdio.h>
#include<queue>
#include<algorithm>
#include<string.h>
#include<iostream>
#define debug(x) cout<<#x<<":"<<x<<endl;
#define dl(x) printf("%lld\n",x);
#define di(x) printf("%d\n",x);
typedef long long ll;
typedef unsigned long long ull;
using namespace std;
const ll INF= 1e18+7;
const ll maxn = 2e5+700;
const int M = 1e6+8;
const ll mod= 1e6+7;
const double eps = 1e-9;
const double PI = acos(-1);
auto random_address = [] { char *p = new char; delete p; return uint64_t(p); };
const uint64_t SEED = chrono::steady_clock::now().time_since_epoch().count() * (random_address() | 1);
mt19937_64 rng(SEED);
template<typename T>inline void read(T &a){char c=getchar();T x=0,f=1;while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+c-'0';c=getchar();}a=f*x;}
ll n,m,p;
ll a[maxn],sum[maxn];
void add(int pos,int val){
	while(pos<=n){
		sum[pos] += val;
		pos += pos&-pos;
	}
}
ll query(int pos){
	ll ans = 0;
	while(pos){
		ans += sum[pos];
		pos -= pos&-pos;
	}return ans;
}
vector<pair<int,int>>q[maxn];
ll res[maxn];
multiset<int>s[maxn];
int main(){
	read(n);read(m);
	for(int i=1;i<=n;i++) read(a[i]);
	for(int i=1;i<=m;i++){
		int x,y;
		read(x);read(y);
		if(x>y) swap(x,y);
		q[y].push_back({x,i});
	}
	for(int i=1;i<=n;i++){
		if(a[i]<=n){
			s[a[i]].insert(i);
			int sz = s[a[i]].size();
			if(sz == a[i]+1){
				int sp = *s[a[i]].begin(),tp = *next(s[a[i]].begin());
				int temp = query(sp)-query(sp-1);
				add(sp,-temp-1);
				add(tp,1);
			}else if(sz == a[i]){
				int sp = *s[a[i]].begin();
				add(sp,1);
			}else if(sz == a[i]+2){
				int sp = *s[a[i]].begin(),tp = *next(s[a[i]].begin()),tpp = *next(next(s[a[i]].begin()));
				int temp = query(sp)-query(sp-1);
				int tempx = query(tp)-query(tp-1);
				add(sp,-temp);
				add(tp,-tempx-1);
				add(tpp,1);
				s[a[i]].erase(sp);
			}
		}
		for(auto x:q[i])
			res[x.second] = query(i)-query(x.first-1);
	}
	for(int i=1;i<=m;i++){
		printf("%lld\n",res[i]);
	}
	return 0;
}
/***
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一只酷酷光儿( CoolGuang)

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值