Codeforces 351(#204 Div1) D. Jeff and Permutation

問題

数列a[i]に対して次の操作を行うことができる。

  • 位置が等差数列になっている同じ値を選ぶ(a[i] = a[i + m] = a[i + 2*m] = ...)
  • この値を数列から削除する
  • 残った数を好きに並び替える


数列b[i]が与えられる。この数列に対して次のクエリm個に答えよ。
区間(li, ri)が与えられる。数列の(li, ri)を抜き出してa[i]とする。
a[i]を上の操作を行って全ての要素を削除するとき、必要な操作の最小回数を求める。

制約条件

b[i]≦10^9
b[i]の要素≦10^5個
m≦10^5

方針

クエリの答えは、区間(li, ri)中の異なる数字の数は最低必要。
どれかの数字の並びが等間隔になっているとき、その数を消せば並び替えで他の数を全部並べられる。そうでないときは+1回余分にかかる。


なので、(li, ri)中のdistinctな値の数と、値が等差数列で出現しているものの個数が(1個以上あるか)わかればよい。


よくある感じで区間クエリを平方分割して頑張る。(l, r)を(l / B, r)の順で並び替える。(B = √n)
すると、左端の調整は一回当たりO(√n)で、それがn回なので合計O(n√n)回。
右端の調整は、一つのバケットでは昇順になっているので、一つのバケットで最大n回。
それが√nバケットなのでO(n√n)回。
で、調整の回数がO(n√n)で抑えられる。


調整はどうやればいいかというと、a[i]の値ごとにdequeを作って、そこに要素を出し入れする。
入れたところの間隔がpos[k] - pos[k - 1] != pos[k - 1] - pos[k - 2]になっていたら、
その不連続点を記録しておく。不連続点もdequeで管理する。
空になるときだけ、異なる要素の数、不連続なa[i]の数を加減する。


これで一回の調整はO(1).
popの判定ほうを先にしてしまって、L≧Rにするというバグを埋め込んでしまい時間内に通せなかった。悲しい。

ソースコード

const int B = 400;
const int MX = 100010;
int n, m, a[MX], ans[MX];
vector<pair<pi, pi> > q; //(bkt, r, l, id)

int pt_cnt, jmp_cnt;
deque<int> jmp[MX], pt[MX];

#define PT pt[val]
#define JM jmp[val]

inline void pop_front(int val){
	PT.pop_front();
	if(PT.empty()) pt_cnt--;
	if(PT.size() && JM.size() && JM.front() == PT.front()){
		JM.pop_front();
		if(JM.empty()) jmp_cnt--;
	}
}
inline void pop_back(int val){
	PT.pop_back();
	if(PT.empty()) pt_cnt--;
	if(PT.size() && JM.size() && JM.back() == PT.back()){
		JM.pop_back();
		if(JM.empty()) jmp_cnt--;
	}
}
inline void push_front(int pos){
	int val = a[pos];
	if(PT.empty()) pt_cnt++;
	PT.push_front(pos);
	if(PT.size() > 2 && PT[1] - PT[0] != PT[2] - PT[1]){
		if(JM.empty()) jmp_cnt++;
		JM.push_front(PT[1]);
	}
}
inline void push_back(int pos){
	int val = a[pos];
	if(PT.empty()) pt_cnt++;
	PT.pb(pos);
	int k = PT.size();
	if(k > 2 && PT[k - 1] - PT[k - 2] != PT[k - 2] - PT[k - 3]){
		if(JM.empty()) jmp_cnt++;
		JM.pb(PT[k - 2]);
	}
}

int main(){
	scanf("%d", &n);
	vi v;
	rep(i, n){
		scanf("%d", a + i);
		v.pb(a[i]);
	}
	sort(all(v)); v.erase(unique(all(v)), v.end());
	rep(i, n) a[i] = lower_bound(all(v), a[i]) - v.begin();
	
	scanf("%d", &m);
	rep(i, m){
		int l, r; scanf("%d%d", &l, &r); --l;
		q.pb(mp(mp(l / B, r), mp(l, i)));
	}
	sort(all(q));
	
	int L = 0, R = 0;
	
	rep(it, m){
		int l = q[it].second.first, r = q[it].first.second;
		int id = q[it].second.second;
		
		while(l < L) push_front(--L);
		while(r > R) push_back(R++);
		while(l > L) pop_front(a[L++]);
		while(r < R) pop_back(a[--R]);
		
		ans[id] = pt_cnt + (jmp_cnt == pt_cnt);
	}
	rep(i, m) printf("%d\n", ans[i]);
	
	return 0;
}