AcWing 251 小Z的袜子 (莫队)

题目链接:https://www.acwing.com/problem/content/description/253/

莫队算法:对询问分块
先将询问按左端点递增排序,然后将询问分成 \(\sqrt{n}\) 块,块内再将询问按右端点递增排序
块内相邻左端点变化不会超过 \(\sqrt{n}\) ,而右端点变化之和为 \(n\)
所以复杂度是 \(O(n\sqrt{n})\)

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<stack>
#include<queue>
using namespace std;
typedef long long ll;

const int maxn = 100010;

int n, m, t, len, tot;
int c[maxn], b[maxn], L[maxn], R[maxn], pos[maxn], cnt[maxn];
ll ans;

struct Q{
	int id;
	int L, R;
	ll ans;
}q[maxn];

bool cmp_L(Q a, Q b){
	if(a.L == b.L) return a.R < b.R;
	return a.L < b.L;
}

bool cmp_R(Q a, Q b){
	if(a.R == b.R) return a.L < b.L;
	return a.R < b.R;
}

bool cmp_id(Q a, Q b){
	return a.id < b.id;
}

void add(int i){
	ans -= 1ll * cnt[c[i]] * (cnt[c[i]] - 1) / 2;
	++cnt[c[i]];
	ans += 1ll * cnt[c[i]] * (cnt[c[i]] - 1) / 2;
}

void del(int i){
	ans -= 1ll * cnt[c[i]] * (cnt[c[i]] - 1) / 2;
	--cnt[c[i]];
	ans += 1ll * cnt[c[i]] * (cnt[c[i]] - 1) / 2;
}

ll gcd(ll a, ll b){ return b ? gcd(b, a % b) : a; }

ll read(){ ll s=0,f=1; char ch=getchar(); while(ch<'0' || ch>'9'){ if(ch=='-') f=-1; ch=getchar(); } while(ch>='0' && ch<='9'){ s=s*10+ch-'0'; ch=getchar(); } return s*f; }

int main(){
	ans = 0;
	n = read(), m = read(); 
	t = sqrt(n); len = m / t;
	//分块预处理 
	for(int i = 1; i <= t; ++i){
		L[i] = (i - 1) * len + 1;
		R[i] = i * len;
	}
	
	for(int i = 1; i <= t; ++i){
		for(int j = L[i] ; j <= R[i]; ++j){
			pos[j] = i;
		}
	}
	if(R[t] < m){ ++t; L[t] = R[t - 1] + 1; R[t] = m; }
	//
	
	// 离散化 
	for(int i = 1; i <= n ; ++i) c[i] = read(), b[i] = c[i];
	
	sort(b + 1, b + 1 + n);
	tot = unique(b + 1, b + 1 + n) - b - 1;
	for(int i = 1 ; i <= n; ++i) c[i] = lower_bound(b + 1, b + 1 + tot, c[i]) - b; 
	
	
	for(int i = 1; i <= m; ++i){
		q[i].L = read(), q[i].R = read();
		q[i].id = i;
	}
	
	sort(q + 1, q + 1 + m, cmp_L);

	int cl, cr;
	for(int i = 1; i <= t; ++i){
		memset(cnt, 0 ,sizeof(cnt));
		ans = 0;
		sort(q + L[i], q + 1 + R[i], cmp_R);
		
		int ql = q[L[i]].L, qr = q[L[i]].R; cl = ql, cr = qr;
		for(int j = ql; j <= qr; ++j) ++cnt[c[j]];
		
		for(int j = 1; j <= tot; ++j) ans += cnt[j] * (cnt[j] - 1) / 2; 
		
		q[L[i]].ans = ans;
		
		for(int j = L[i] + 1; j <= R[i]; ++j){
			ql = q[j].L, qr = q[j].R;
			while(cl > ql) add(cl - 1), --cl;
			while(cl < ql) del(cl), ++cl;
			while(cr < qr) add(cr + 1), ++cr;
			
			q[j].ans = ans;
		}
	} 
	
	sort(q + 1, q + 1 + m, cmp_id);
	
	for(int i = 1; i <= m; ++i){
		ll A = q[i].ans, B = q[i].R - q[i].L + 1;
		B = 1ll * B * (B - 1) / 2;
		ll gc = gcd(A, B);
		
		if(A == 0) printf("0/1\n");
		else printf("%lld/%lld\n", A / gc, B / gc);
	}
	
	return 0;
}
posted @ 2020-11-12 10:57  Tartarus_li  阅读(116)  评论(0编辑  收藏  举报