P1494 [国家集训队] 小 Z 的袜子

P1494 [国家集训队] 小 Z 的袜子
莫队模板.
不难得出,对于一个长度为n的区间,所求即为:(区间内每种颜色数目的平方和 - n) / (n(n-1))
关键在于维护区间内每种颜色数目的平方和,
把区间分成O(√n)块,每个位置记录所属块pos,
对问题按第一关键字为pos, 第二关键字为r排序,从而保证时间复杂度为O(n√n)
每个问题答案在上一个问题基础上增减,代码如下:

#include <cstdio>
#include <cctype>
#include <cmath>
#include <algorithm>

using namespace std;

#define ll long long

inline int read(){
    int res = 0, pdf = 0; char ch = getchar();
    while(!isdigit(ch)) pdf = ch == '-', ch = getchar();
    while(isdigit(ch)) res = (res<<3) + (res<<1) + (ch^48), ch = getchar();
    return pdf ? -res : res;
} 

inline void Print(ll x) {
	if (x < 0) x = -x, putchar('-'); 
	if (x < 10) putchar(x + '0'); 
	else {
		Print(x / 10); 
		putchar(x % 10 + '0'); 
	}
} 

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

const int N = 5e5 + 10; 
int n, m, block, col[N], pos[N]; ll num[N]; 
ll ans; 

struct Ask {
	int l, r, id; 
	ll fz, fm; 
};
Ask a[N]; 

void update(int p, ll add) {
	ans -= num[col[p]] * num[col[p]]; 
	num[col[p]] += add; 
	ans += num[col[p]] * num[col[p]]; 
}

void Solve() {
	ll l = 1, r = 0; 
	for (int i = 1; i <= m; ++i) {
		while (a[i].l < l) { update(--l, 1); }
		while (a[i].l > l) { update(l++, -1); }
		while (a[i].r < r) { update(r--, -1); }
		while (a[i].r > r) { update(++r, 1); }
		if (a[i].l == a[i].r) {
			a[i].fz = 0; a[i].fm = 1; 
			continue; 
		} 
		a[i].fz = ans - (r - l + 1); 
		a[i].fm = (r - l + 1) * (r - l); 
		ll GCD = gcd(a[i].fz, a[i].fm); 
		a[i].fz /= GCD; a[i].fm /= GCD;  
	}
}

bool cmp1(Ask x, Ask y) {
	if (pos[x.l] == pos[y.l]) return x.r < y.r; 
	else return pos[x.l] < pos[y.l]; 
}

bool cmp2(Ask x, Ask y) {
 	return x.id < y.id; 
}

int main() {
	n = read(); m = read(); 
	block = sqrt(n); 
	for (int i = 1; i <= n; ++i) {
		col[i] = read(); 
		pos[i] = (i - 1) / block + 1; 
	}
	for (int i = 1; i <= m; ++i) {
		a[i].l = read(); a[i].r = read(); 
		a[i].id = i; 
	} 
	sort(a + 1, a + m + 1, cmp1); 
	Solve(); 
	sort(a + 1, a + m + 1, cmp2); 
	for (int i = 1; i <= m; ++i) {
		Print(a[i].fz); putchar('/'); Print(a[i].fm); putchar('\n'); 
	}
	return 0; 
}

补充:
1.序列长度为 n n n, 询问次数为 m m m, 块长为 S S S, 则时间复杂度为 O ( n 2 S + m S ) O(\frac {n^2} {S} + mS) O(Sn2+mS), m S mS mS是因为移动可能跨越块,当 S = n m S=\frac {n} {\sqrt{m}} S=m n时时间复杂度最优,为 O ( n m ) O(n\sqrt{m}) O(nm ).
2.奇偶化排序即对于属于奇数块的询问,r 按从小到大排序,对于属于偶数块的排序,r 从大到小排序,这样我们的 r 指针在处理完这个奇数块的问题后,将在返回的途中处理偶数块的问题,再向 n 移动处理下一个奇数块的问题,优化了 r 指针的移动次数,一般情况下,这种优化能让程序快 30% 左右。
3.小细节:如果使用 sort 比较两个函数,不能出现 a > b a>b a>b a < b a<b a<b 同时为真的情况,否则会运行错误。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值