题目
题目大意:
给你一个等差序列,每次查询一段区间 [ l , r ] [l,r] [l,r]的答案。 显然这是典型的不带修改的区间询问类问题,我们可以考虑用莫队算法去解决。
解题思路:
接下来看怎么递推 [ l , r ] [l,r] [l,r]到 [ l − 1 , r ] [l-1,r] [l−1,r], [ l , r + 1 ] [l,r+1] [l,r+1]的关系
首先对区间的询问是: 每次 任选 a i a_i ai, k k k,把 a i ai ai删掉,如 a i + k = = a i a_{i+k}==a_{i} ai+k==ai则一直删下去,也就是把值相等,且下标形成等差数列的一列数删掉, 余下的数重新排列。
问你删除完整个数列的最少操作数
显然,第一次删掉后重排的话,我们肯定可以把所有数排成公差为1的一个个等差数列。
-
所以其实 询问的意思就是: 如果第一次能找到一个数值,其所有出现的数的下标都形成等差数列(可以一次性删除),那么之后的操作步数实际就是 数值的种类数-1+1。
因此 答案是 总种类数;
- 如果第一次找不到 一个数满足 所有出现的数的下标都形成等差数列 则答案就是 总的种类数+1
- 转移的话,需要预处理两个数组, fl[i]和fr[i],fl[i]表示i位置往左找,第一个使得 a [ i ] a[i] a[i]不满足等差数列的位置,fr同理。
根据以下递推式:
inline void init() {
for(int i = 1; i <= n; ++ i) {
int p = arr[i];
mp[p] = 1;
pre[i] = last[p]; // 前驱
last[p] = i;
if(pre[i] - pre[pre[i]] == i - pre[i] || pre[pre[i]] == 0)
fl[i] = fl[pre[i]];
else fl[i] = pre[pre[i]];
}
for(auto it : mp) last[it.first] = n + 1;
fr[n+1] = n+1;
bak[0] = n+1;
bak[n+1] = n+1;
for(int i = n; i >= 1; -- i) {
int p = arr[i];
bak[i] = last[p]; // 后继
last[p] = i;
if(bak[bak[i]] - bak[i] == bak[i] - i || bak[bak[i]] == n + 1)
fr[i] = fr[bak[i]];
else fr[i] = bak[bak[i]];
}
}
预处理完后,
按照莫队算法,如果上一次区间为 [ l , r ] [l,r] [l,r],那么下一次要add一个数的话,
如果 A d d Add Add的数第一次出现,则kind++, 且num_dengcha++
否则,看该数的加入是否会导致 num_dengcha减少。
即:if(fl[i]>=x&&fl[pre[i]]<x) dengcha–; 之前是等差,现在不是等差
如果是 S u b Sub Sub的话,
如果这个数只有一个,则显然kind–,num_dengcha–;
否则,看该数的减少会不会导致 num_dengcha的增加
即:if (fr[bak[i]]>R&&fr[i]<=R) dengcha++;
好了,接下来按照套路写莫队就好了
AC code
#include <bits/stdc++.h>
#define mid ((l + r) >> 1)
#define Lson rt << 1, l , mid
#define Rson rt << 1|1, mid + 1, r
#define ms(a,al) memset(a,al,sizeof(a))
#define log2(a) log(a)/log(2)
#define lowbit(x) ((-x) & x)
#define IOS std::ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
#define INF 0x3f3f3f3f
#define LLF 0x3f3f3f3f3f3f3f3f
#define f first
#define s second
#define endl '\n'
using namespace std;
const int N = 2e6 + 10, mod = 1e9 + 9;
const int maxn = 500010;
const long double eps = 1e-5;
const int EPS = 500 * 500;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> PII;
typedef pair<ll,ll> PLL;
typedef pair<double,double> PDD;
template<typename T> void read(T &x) {
x = 0;char ch = getchar();ll f = 1;
while(!isdigit(ch)){if(ch == '-')f*=-1;ch=getchar();}
while(isdigit(ch)){x = x*10+ch-48;ch=getchar();}x*=f;
}
template<typename T, typename... Args> void read(T &first, Args& ... args) {
read(first);
read(args...);
}
int n, m, arr[maxn];
int fl[maxn], fr[maxn];
int pre[maxn], bak[maxn], last[maxn];
unordered_map<int,bool> mp;
inline void init() {
for(int i = 1; i <= n; ++ i) {
int p = arr[i];
mp[p] = 1;
pre[i] = last[p];
last[p] = i;
if(pre[i] - pre[pre[i]] == i - pre[i] || pre[pre[i]] == 0)
fl[i] = fl[pre[i]];
else fl[i] = pre[pre[i]];
}
for(auto it : mp) last[it.first] = n + 1;
fr[n+1] = n+1;
bak[0] = n+1;
bak[n+1] = n+1;
for(int i = n; i >= 1; -- i) {
int p = arr[i];
bak[i] = last[p];
last[p] = i;
if(bak[bak[i]] - bak[i] == bak[i] - i || bak[bak[i]] == n + 1)
fr[i] = fr[bak[i]];
else fr[i] = bak[bak[i]];
}
// for(int i = 1; i <= n; ++ i)
// cout << fl[i] << " \n"[i == n];
// for(int i = 1; i <= n; ++ i)
// cout << pre[i] << " \n"[i == n];
// for(int i = 1; i <= n; ++ i)
// cout << fr[i] << " \n"[i == n];
// for(int i = 1; i <= n; ++ i)
// cout << bak[i] << " \n"[i == n];
}
struct node {
int l, r, id;
int b;
}q[maxn];
bool cmp1(node x,node y) {
return x.b^y.b?x.b<y.b:x.b&1?x.r<y.r:x.r>y.r;
}
int ans[maxn], res = 0, is;
int cnt[maxn];
inline void Add(int u, bool lorr, int i) {
if(++ cnt[arr[u]] == 1) res ++, is ++;
else {
if(lorr) {// 这是判断是左右指针哪个在移动
if(fr[bak[u]] > i && fr[u] <= i) is --; //之前是等差,现在不是等差,
} else {
if(fl[pre[u]] < i && fl[u] >= i) is --;
}
}
}
inline void Sub(int u, bool lorr, int i) {
if(-- cnt[arr[u]] == 0) res --, is --;
else {
if(lorr) {// 这是判断是左右指针哪个在移动
if(fr[bak[u]] > i && fr[u] <= i) is ++;
} else {
if(fl[pre[u]] < i && fl[u] >= i) is ++;
}
}
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n;
int block = 500;
for(int i = 1; i <= n; ++ i) cin >> arr[i];
init();
cin >> m;
for(int i = 1; i <= m; ++ i) {
int l, r;
cin >> l >> r;
q[i] = {l,r,i,(l-1)/block+1};
}
sort(q+1,q+1+m,cmp1);
int l = 1, r = 0;
for(int i = 1; i <= m; ++ i) {
while(q[i].l < l) Add(--l,1,r);// 上个区间的r
while(q[i].r > r) Add(++r,0,l);// 上个区间的l
while(q[i].l > l) Sub(l++,1,r);
while(q[i].r < r) Sub(r--,0,l);
ans[q[i].id] = res - (is!=0) + 1;
}
for(int i = 1; i <= m; ++ i)
cout << ans[i] << "\n";
return 0;
}
/*
10
2 1 3 3 3 3 1 3 1 1
1
4 8
*/