首页 > 代码库 > BZOJ 2038 2009国家集训队 小Z的袜子(hose) 莫队算法

BZOJ 2038 2009国家集训队 小Z的袜子(hose) 莫队算法

题目大意:给定n个点,每个点有一个颜色,m次询问,每次询问一个区间内随机选出两个点颜色相同的概率是多少

OTZ莫队算法……

具体做法无论是分块还是曼哈顿最小生成树网上都讲解的很详细 我就不累述了

这个题的做法是记录一个cnt数组表示当前区间内每种颜色有多少个

加入一个颜色为x的点就ans+=cnt[x] 然后cnt[x]++

减少一个颜色为x的点就cnt[x]-- 然后ans-=cnt[x]

于是添加或删除一个点O(1)出解 这里拎出来解释一下 因为我代码里写的表达式太凶残了

我两个版本都写了一遍……然后这就是结果


上面是曼哈顿最小生成树,下面是分块

于是分块无论是时间上还是代码复杂度上都完爆MMST……果断分块大法好啊0.0

不过MMST写着真心高大上0.0 给人一种【哇塞我连这种高端算法都搞定了あたいは最強ね!】的错觉QAQ

总之两个版本都贴上来吧 对比一下

分块:

#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define M 50500
using namespace std;
struct query{
	int l,r,pos;
	bool operator < (const query &x) const;
}q[M];
int n,m,block;
int a[M],belong[M],cnt[M];
pair<unsigned int,unsigned int> ans[M];
unsigned int now;
bool query :: operator < (const query &x) const
{
	if(belong[l]==belong[x.l])
		return r<x.r;
	return belong[l]<belong[x.l];
}
int main()
{
	#ifdef PoPoQQQ
		freopen("2038.in","r",stdin);
		freopen("2038.out","w",stdout);
	#endif

	int i;
	cin>>n>>m;
	for(i=1;i<=n;i++)
		scanf("%d",&a[i]);
	for(i=1;i<=m;i++)
		scanf("%d%d",&q[i].l,&q[i].r),q[i].pos=i;
	block=static_cast<int>(ceil(sqrt(n)));
	for(i=1;i<=n;i++)
		belong[i]=(i-1)/block+1;
	sort(q+1,q+m+1);
	int l=1,r=0;
	for(i=1;i<=m;i++)
	{
		while(r<q[i].r)
			now+=cnt[a[++r]]++;
		while(r>q[i].r)
			now-=--cnt[a[r--]];
		while(l<q[i].l)
			now-=--cnt[a[l++]];
		while(l>q[i].l)
			now+=cnt[a[--l]]++;
		unsigned int temp=(unsigned)(r-l+1)*(r-l)>>1;
		unsigned int gcd=__gcd(now,temp);
		ans[q[i].pos]=make_pair(now/gcd,temp/gcd);
	}
	for(i=1;i<=m;i++)
		printf("%u/%u\n",ans[i].first,ans[i].second);
}

曼哈顿最小生成树

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define M 50500
using namespace std;
struct edge{
	int x,y,f;
	bool operator < (const edge &z) const
	{
		return f < z.f;
	}
}e[M<<2];
struct point{
	int x,y,pos,key;
	bool operator < (const point &p) const
	{
		if(x==p.x)
			return y>p.y;
		return x>p.x;
	}
}points[M];
struct query{
	int l,r;
}q[M];
struct abcd{
	int to,next;
}table[M<<1];
int head[M],tot;
int n,m,k,a[M],c[M];
int fa[M];
int l=1,r=0,cnt[M];
unsigned int now;
pair<unsigned int,unsigned int>ans[M];
int Find(int x)
{
	if(!fa[x]||fa[x]==x)
		return fa[x]=x;
	return fa[x]=Find(fa[x]);
}
void Add(int x,int y)
{
	table[++tot].to=y;
	table[tot].next=head[x];
	head[x]=tot;
}
void Update(int x,int y)
{
	for(;x;x-=x&-x)
		if(points[y].x+points[y].y<points[c[x]].x+points[c[x]].y)
			c[x]=y;
}
int Get_Ans(int x)
{
	int re=0;
	for(;x<=m;x+=x&-x)
		if(points[c[x]].x+points[c[x]].y<points[re].x+points[re].y)
			re=c[x];
	return re;
}
void Calculate()
{
	int i,temp=0;
	static pair<int,int>b[M];
	for(i=1;i<=m;i++)
		b[i]=make_pair(points[i].y-points[i].x,i);
	sort(b+1,b+m+1);
	for(i=1;i<=m;i++)
	{
		if(i==1||b[i].first!=b[i-1].first)
			++temp;
		points[b[i].second].key=temp;
	}
	sort(points+1,points+m+1);
	memset(c,0,sizeof c);
	for(i=1;i<=m;i++)
	{
		temp=Get_Ans(points[i].key);
		if(temp)
		{
			e[++k].f=points[temp].x+points[temp].y-points[i].x-points[i].y;
			e[k].x=points[i].pos;e[k].y=points[temp].pos;
		}
		Update(points[i].key,i);
	}
}
void Kruskal()
{
	int i;
	sort(e+1,e+k+1);
	for(i=1;i<=k;i++)
	{
		int x=e[i].x,y=e[i].y;
		if(Find(x)!=Find(y))
		{
			fa[Find(x)]=Find(y);
			Add(x,y),Add(y,x);
		}
	}
}
void DFS(int x,int from)
{
	int i;
	
	while(r<q[x].r)
		now+=cnt[a[++r]]++;
	while(l>q[x].l)
		now+=cnt[a[--l]]++;
	while(r>q[x].r)
		now-=--cnt[a[r--]];
	while(l<q[x].l)
		now-=--cnt[a[l++]];
	
	unsigned int temp=(unsigned)(r-l+1)*(r-l)>>1;
	unsigned int gcd=__gcd(now,temp);
	ans[x]=make_pair(now/gcd,temp/gcd);
	
	for(i=head[x];i;i=table[i].next)
	{
		if(table[i].to==from)
			continue;
		DFS(table[i].to,x);
		
		while(r<q[x].r)
			now+=cnt[a[++r]]++;
		while(l>q[x].l)
			now+=cnt[a[--l]]++;
		while(r>q[x].r)
			now-=--cnt[a[r--]];
		while(l<q[x].l)
			now-=--cnt[a[l++]];
		
	}
}
int main()
{

	#ifdef PoPoQQQ
		freopen("2038.in","r",stdin);
		freopen("2038.out","w",stdout);
	#endif

	int i;
	cin>>n>>m;
	for(i=1;i<=n;i++)
		scanf("%d",&a[i]);
	for(i=1;i<=m;i++)
	{
		scanf("%d%d",&q[i].l,&q[i].r);
		points[i].x=q[i].l;
		points[i].y=q[i].r;
		points[i].pos=i;
	}
	points[0].x=points[0].y=0x3f3f3f3f;
	Calculate();
	for(i=1;i<=m;i++)
		points[i].y=-points[i].y;
	Calculate();
	for(i=1;i<=m;i++)
		points[i].y=-points[i].y,swap(points[i].x,points[i].y);
	Calculate();
	for(i=1;i<=m;i++)
		points[i].y=-points[i].y;
	Calculate();
	Kruskal();
	DFS(1,0);
	for(i=1;i<=m;i++)
		printf("%u/%u\n",ans[i].first,ans[i].second);
}


BZOJ 2038 2009国家集训队 小Z的袜子(hose) 莫队算法