luogu P1494 [国家集训队]小Z的袜子
题面传送门
其实个人感觉这道题如果能用莫队过评分偏高(如果您是主席树大佬当我没说)
这道题用莫队怎么过呢?
我们可以分析一下式子
我们以\(a_i\)代表区间内颜色为\(i\)的袜子有几双,那么答案是\(\dfrac{\sum\limits_{i=1}^{n}{\dfrac{a_i*(a_i-1)}{2}}}{\dfrac{(r-l+1)(r-l)}{2}}\)
拆一下式子,得到\(\dfrac{\sum\limits_{i=1}^{n}{a_i^2}-(r-l-1)}{(r-l+1)(r-l)}\)
那么这道题变成了求区间内每种颜色的平方和。这可是莫队的板子
怎么解详见这一篇
代码实现:
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
long long n,m,ans1[200039],ans2[200039],tot,pus,now,k,f[200039],l,r,a[200039],tmp,x,y,z[200039];
struct yyy {
long long x,y,num;
} s[200039];
inline bool cmp(yyy a,yyy b) {
return (f[a.x]^f[b.x])?(f[a.x]<f[b.x]):((f[a.x]&1)?a.y<b.y:a.y>b.y);
}
inline void read(long long &x) {
char s=getchar();int f=1;x=0;
while(s<'0'||s>'9') {if(s=='-') f=-1;s=getchar();}
while(s>='0'&&s<='9') x=(x<<3)+(x<<1)+(s^48),s=getchar();
x*=f;
}
int main() {
register int i,j;
scanf("%lld%lld",&n,&m);
k=sqrt(n);
for(i=1; i<=k; i++) f[i]=1;
for(i=k+1; i<=n; i++) f[i]=f[i-k]+1;
for(i=1; i<=n; i++) read(a[i]);
for(i=1; i<=m; i++) {
read(x);
read(y);
if(x==y) ans2[i]=1;
else s[++tmp]=(yyy) {x,y,i};
}
sort(s+1,s+tmp+1,cmp);
for(i=s[1].x; i<=s[1].y; i++) {
tot+=z[a[i]]*2+1;
z[a[i]]++;
}
l=s[1].x;r=s[1].y;
pus=(s[1].y-s[1].x+1)*(s[1].y-s[1].x);
now=__gcd(pus,(tot-(s[1].y-s[1].x+1)));
ans1[s[1].num]=(tot-(s[1].y-s[1].x+1))/now;
ans2[s[1].num]=pus/now;
//printf("%lld\n",tot);
for(i=2; i<=tmp; i++) {
while(l<s[i].x) tot-=z[a[l]]*2-1,z[a[l]]--,l++;
while(r>s[i].y) tot-=z[a[r]]*2-1,z[a[r]]--,r--;
while(l>s[i].x) l--,tot+=z[a[l]]*2+1,z[a[l]]++;
while(r<s[i].y) r++,tot+=z[a[r]]*2+1,z[a[r]]++;
//printf("%lld\n",tot);
if(tot-(s[i].y-s[i].x+1)==0){ans1[s[i].num]=0;ans2[s[i].num]=1;continue;}
pus=(s[i].y-s[i].x+1)*(s[i].y-s[i].x);
now=__gcd(pus,tot-(s[i].y-s[i].x+1));
ans1[s[i].num]=(tot-(s[i].y-s[i].x+1))/now;
ans2[s[i].num]=pus/now;
}
for(i=1;i<=m;i++) printf("%lld/%lld\n",ans1[i],ans2[i]);
}
时间复杂度\(O((n+m)\sqrt nlog^2n)\)