[bzoj2038][2009国家集训队]小Z的袜子(hose)_莫队

小Z的袜子 hose 2009-国家集训队 bzoj-2038

题目大意:给定一个n个袜子的序列,每个袜子有一个颜色。m次询问:每次询问一段区间中每种颜色袜子个数的平方和。

注释:$1\le n,m\le 5\cdot 10^4$。


想法

莫队算法的第一道例题。

每次左指针和右指针动的时候注意平方即可,

最后,附上丑陋的代码... ...

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
#define N 80050
struct Node
{
	int s,t,id;
	int ans;
}a[N];
int now;
int n,q,block,pos[N],h[N],c[N];
inline bool cmp1(const Node &x,const Node &y)
{
	if(pos[x.s]==pos[y.s]) return x.t<y.t;
	return pos[x.s]<pos[y.s];
}
inline bool cmp2(const Node &x,const Node &y)
{
	return x.id<y.id;
}
void update(int x,int sig)
{
	if(sig==1)
	{
		now+=h[c[x]];
		h[c[x]]++;
	}
	else
	{
		now-=h[c[x]]-1;
		h[c[x]]--;
	}
	/*now-=h[c[x]];
	h[c[x]]+=sig;
	now+=h[c[x]];*/
}
void print(int x)
{
	int b=a[x].ans,d=1ll*(a[x].t-a[x].s)*(a[x].t-a[x].s+1)/2;
	if(b==0)
	{
		puts("0/1");return;
	}
	int gcd=__gcd(b,d);
	printf("%d/%d\n",b/gcd,d/gcd);
}
int main()
{
	scanf("%d%d",&n,&q);
	int i,l,r=0,j;
	for(i=1;i<=n;++i) scanf("%d",&c[i]);
	if(!n)return 0;
	int size=sqrt(n);
	block=n/size;
	for(i=1;i<=block;++i)
	{
		l=r+1;
		r=i*size;
		for(j=l;j<=r;++j)
		{
			pos[j]=i;
		}
	}
	if(r!=n)
	{
		l=r+1;
		r=n;
		block++;
		for(i=l;i<=r;++i)
		{
			pos[i]=block;
		}
	}
	for(i=1;i<=q;++i)scanf("%d%d",&a[i].s,&a[i].t),a[i].id=i;
	sort(a+1,a+q+1,cmp1);
	for(l=1,r=0,i=1;i<=q;++i)
	{
		if(a[i].s==a[i].t)
		{
			a[i].ans=0;
			continue;
		}
		while(l<a[i].s)update(l,-1),l++;
		while(l>a[i].s)update(l-1,1),l--;
		while(r<a[i].t)update(r+1,1),r++;
		while(r>a[i].t)update(r,-1),r--;
		a[i].ans=now;
	}
	sort(a+1,a+q+1,cmp2);
	for(i=1;i<=q;i++)
	{
		print(i);
	}
}

小结:莫队真可爱/ka

posted @ 2018-09-17 14:45  JZYshuraK_彧  阅读(160)  评论(0编辑  收藏  举报