题目大意:给定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);
}