BZOJ P2038 [2009国家集训队] 小Z的袜子【莫队】

首先让我们考虑一下答案的表达式。

设当前的询问区间为 [ L e f t , R i g h t ] [Left,Right] [Left,Right],且在当前区间中第 i i i种袜子有 N u m [ i ] Num[i] Num[i]支,那么 A n s = C N u m [ 1 ] 2 + C N u m [ 2 ] 2 + C N u m [ 3 ] + . . . 2 C R i g h t + 1 − L e f t 2 Ans=\frac{C^2_{Num[1]}+C^2_{Num[2]}+C^2_{Num[3]+...}}{C^2_{Right+1-Left}} Ans=CRight+1Left2CNum[1]2+CNum[2]2+CNum[3]+...2

L e n g t h = R i g h t + 1 − L e f t Length=Right+1-Left Length=Right+1Left

展开组合数:
A n s = N u m [ 1 ] 2 + N u m [ 2 ] 2 + N u m [ 3 ] 2 + . . . − L e n g t h L e n g t h ∗ ( L e n g t h − 1 ) Ans=\frac{Num[1]^2+Num[2]^2+Num[3]^2+...-Length}{Length*(Length-1)} Ans=Length(Length1)Num[1]2+Num[2]2+Num[3]2+...Length

于是我们现在的任务就是求得当前区间的 N u m [ i ] Num[i] Num[i]即可,莫队搞一搞,注意特判与最后的分数化为最简。

参考代码:

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define SG string
#define DB double
#define LL long long
using namespace std;
const LL Max=5e4+5;
struct Node{
	LL X,Y,Id;
}Ask[Max];
LL N,M,S,Tot=1ll,A[Max],B[Max],Cnt[Max],Ans_X[Max],Ans_Y[Max];
inline LL Read(){
	LL X=0;char CH=getchar();bool F=0;
	while(CH>'9'||CH<'0'){if(CH=='-')F=1;CH=getchar();}
	while(CH>='0'&&CH<='9'){X=(X<<1)+(X<<3)+CH-'0';CH=getchar();}
	return F?-X:X;
}
inline void Write(LL X){
	if(X<0)X=-X,putchar('-');
	if(X>9)Write(X/10);
	putchar(X%10+48);
}
bool Cmp(Node P,Node Q){
	return B[P.X]==B[Q.X]?P.Y<Q.Y:B[P.X]<B[Q.X];
}
LL GCD(LL X,LL Y){
	while(Y^=X^=Y^=X%=Y);
	return X;
}
void Insert(LL X){
	Tot-=Cnt[X]*Cnt[X];++Cnt[X];Tot+=Cnt[X]*Cnt[X];
}
void Delete(LL X){
	Tot-=Cnt[X]*Cnt[X];--Cnt[X];Tot+=Cnt[X]*Cnt[X];
}
int main(){
	LL I,J,K;
	N=Read(),M=Read();S=sqrt(N);
	for(I=1;I<=N;I++){
		A[I]=Read();
		B[I]=I/S;
	}
	for(I=1;I<=M;I++){
		Ask[I].Id=I;
		Ask[I].X=Read();
		Ask[I].Y=Read();
	}
	sort(Ask+1,Ask+1+M,Cmp);
	LL Left=1ll,Right=1ll;++Cnt[A[1]];
	for(I=1;I<=M;I++){
		if(Ask[I].X==Ask[I].Y){
			Ans_X[Ask[I].Id]=0;
			Ans_Y[Ask[I].Id]=1;continue;
		}
		while(Right<Ask[I].Y){
			Insert(A[++Right]);
		}
		while(Right>Ask[I].Y){
			Delete(A[Right--]);
		}
		while(Left<Ask[I].X){
			Delete(A[Left++]);
		}
		while(Left>Ask[I].X){
			Insert(A[--Left]);
		}
		LL Length=Ask[I].Y+1-Ask[I].X;
		Ans_X[Ask[I].Id]=Tot-Length;
		Ans_Y[Ask[I].Id]=Length*(Length-1);
		LL _GCD=GCD(Ans_X[Ask[I].Id],Ans_Y[Ask[I].Id]);
		Ans_X[Ask[I].Id]/=_GCD;
		Ans_Y[Ask[I].Id]/=_GCD;
	}
	for(I=1;I<=M;I++){
		Write(Ans_X[I]),putchar('/'),Write(Ans_Y[I]),putchar('\n');
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值