码迷,mamicode.com
首页 > 编程语言 > 详细

【BZOJ3781、2038】莫队算法2水题

时间:2017-05-03 21:15:47      阅读:253      评论:0      收藏:0      [点我收藏+]

标签:mil   ges   string   span   移动   names   sha   soft   初学   

【BZOJ3781】小B的询问

题意:有一个序列,包含N个1~K之间的整数。他一共有M个询问,每个询问给定一个区间[L..R],求Sigma(c(i)^2)的值,其中i的值从1到K,其中c(i)表示数字i在[L..R]中的重复次数

题解:初学莫队算法,差不多明白了用莫队的情况,对于这种离线的,区间长度+1时可O(1)修改答案的题,运用莫队算法是最水的

将n分成sqrt(n)块,将询问按照左端点所在的块为第一关键字,右端点的具体位置为第二关键字排序,然后用指针l,r不断暴力平移到询问的左右端点处,并不断O(1)更新答案,这样左端点的移动距离最多为sqrt(n)*m+sqrt(n)*sqrt(n)次,右端点移动距离最多为sqrt(n)*n次,所以总复杂度为O(n*sqrt(n))

对于本题,设原区间为[l,r],我们以从[l,r]平移到[l,r+1]举例,由于只差了r+1这个数,我们设r+1这个数在[l,r]里的出现的次数为c(i),那么原来这个数对答案的贡献为c(i)*c(i),加上r+1后贡献变成了(c(i)+1)*(c(i)+1),那么r+1这一个数的贡献就是2*c(i)+1,然后用它更新答案,最后c(i)++就好了

 

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
using namespace std;
const int maxn=50010;
typedef long long ll;
struct node
{
	int qa,qb,ans,org;
}q[maxn];
int n,m,siz;
ll sum;
int c[maxn];
ll s[maxn];
int gcd(int a,int b)
{
	return (b==0)?a:gcd(b,a%b);
}
bool cmp(node a,node b)
{
	if((a.qa-1)/siz==(b.qa-1)/siz)	return a.qb<b.qb;
	return (a.qa-1)/siz<(b.qa-1)/siz;
}
bool cmp2(node a,node b)
{
	return a.org<b.org;
}
void add(int x)
{
	sum+=s[c[x]],s[c[x]]++;
}
void rem(int x)
{
	s[c[x]]--,sum-=s[c[x]];
}
int main()
{
	scanf("%d%d",&n,&m);
	siz=(int)sqrt((double)n);
	int i;
	ll a,b,g;
	for(i=1;i<=n;i++)	scanf("%d",&c[i]);
	for(i=1;i<=m;i++)	scanf("%d%d",&q[i].qa,&q[i].qb),q[i].org=i;
	sort(q+1,q+m+1,cmp);
	int l=1,r=0;
	for(i=1;i<=m;i++)
	{
		while(r<q[i].qb)	add(++r);
		while(r>q[i].qb)	rem(r--);
		while(l<q[i].qa)	rem(l++);
		while(l>q[i].qa)	add(--l);
		q[i].ans=sum;
	}
	sort(q+1,q+m+1,cmp2);
	for(i=1;i<=m;i++)
	{
		a=q[i].ans,b=(ll)(q[i].qb-q[i].qa)*(q[i].qb-q[i].qa+1)/2,g=gcd(a,b);
		if(a==0)	printf("0/1\n");
		else	printf("%lld/%lld\n",a/g,b/g);
	}
	return 0;
}

 

【BZOJ2038】[2009国家集训队]小Z的袜子(hose)

题意:有n个数,m个询问,每次询问求区间[l,r]中任取两个数使得这两个数相同的概率

题解:别的和上题都一样,只不过答案变成了:(s(i)表示i出现的次数)

技术分享

忽视分母,对于新加的r+1,显然贡献是s(i),然后s(i)++

这题我long long 开得好像不太对~

 

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
using namespace std;
const int maxn=50010;
typedef long long ll;
struct node
{
	int qa,qb,ans,org;
}q[maxn];
int n,m,siz;
ll sum;
int c[maxn];
ll s[maxn];
int gcd(int a,int b)
{
	return (b==0)?a:gcd(b,a%b);
}
bool cmp(node a,node b)
{
	if((a.qa-1)/siz==(b.qa-1)/siz)	return a.qb<b.qb;
	return (a.qa-1)/siz<(b.qa-1)/siz;
}
bool cmp2(node a,node b)
{
	return a.org<b.org;
}
void add(int x)
{
	sum+=s[c[x]],s[c[x]]++;
}
void rem(int x)
{
	s[c[x]]--,sum-=s[c[x]];
}
int main()
{
	scanf("%d%d",&n,&m);
	siz=(int)sqrt((double)n);
	int i;
	ll a,b,g;
	for(i=1;i<=n;i++)	scanf("%d",&c[i]);
	for(i=1;i<=m;i++)	scanf("%d%d",&q[i].qa,&q[i].qb),q[i].org=i;
	sort(q+1,q+m+1,cmp);
	int l=1,r=0;
	for(i=1;i<=m;i++)
	{
		while(r<q[i].qb)	add(++r);
		while(r>q[i].qb)	rem(r--);
		while(l<q[i].qa)	rem(l++);
		while(l>q[i].qa)	add(--l);
		q[i].ans=sum;
	}
	sort(q+1,q+m+1,cmp2);
	for(i=1;i<=m;i++)
	{
		a=q[i].ans,b=(ll)(q[i].qb-q[i].qa)*(q[i].qb-q[i].qa+1)/2,g=gcd(a,b);
		if(a==0)	printf("0/1\n");
		else	printf("%lld/%lld\n",a/g,b/g);
	}
	return 0;
}

 

【BZOJ3781、2038】莫队算法2水题

标签:mil   ges   string   span   移动   names   sha   soft   初学   

原文地址:http://www.cnblogs.com/CQzhangyu/p/6803872.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!