HYSBZ - 2038:小Z的袜子(hose)(莫队算法)
程序员文章站
2023-12-26 13:44:21
...
题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=2038
题目大意:
中文题意,就不多解释了。
解题思路:
也是专门为了学莫队算法才写的这道题,学习莫队算法之后真的是对大神佩服的五体投地,这种算法真的是太优雅了。。。
关于莫队算法网上也有很多博客说的很清楚,这里也就不写太详细了。
莫队算法目前看来想解决问题需要的前置条件就是在知道 区间(l,r)的情况下,能在O(1)时间内得出(l-1,r)(l+1,r)(l,r-1)(l,r+1),
原理呢我个人理解就是先将所有查询拍序,然后通过区间的变化依次得到所有查询的答案存起来最后依次输出即可。
复杂度貌似是n^1.5次方,我个人也不会证明。。。。
那么对于这道题呢,其实就是在某个区间内抽到两个同色袜子的概率。
分母肯定就是区间长度len*(len-1),分子呢其实就是通过当前区间的答案,然后当区间+1或者-1的时候,判断哪种颜色的袜子改变了。然后改变这种颜色的袜子在答案中的贡献即可。
关于公式的推导这里借用一下大佬的推导:http://www.cnblogs.com/MashiroSky/p/5914637.html(自己写太麻烦了,= = 逃~
有了上面的公式就是一步步得到所有答案了,以下贴代码
#include <bits/stdc++.h>
#define rank ra
#define lson rt<<1
#define rson rt<<1|1
#define pb push_back
using namespace std;
typedef long long ll;
const int N=50005;
int n,m,step,cnt,sz;
struct node //记录询问
{
int l,r,id;
}qu[N];
struct Node //记录分子和分母
{
ll l,r;
}ans[N];
int L=1,R=0,a[N],pos[N],num[N];
ll res;
bool cmp(node p,node q) //分块排序
{
if(pos[p.l]==pos[q.l])
return p.r<q.r;
return p.l<q.l;
}
ll gcd(ll a, ll b)
{
return b == 0 ? a : gcd(b, a%b);
}
void del(int k) //删除当前点对答案的贡献
{
res=res-(ll)(num[a[k]]*num[a[k]]);
num[a[k]]--;
res=res+(ll)(num[a[k]]*num[a[k]]);
}
void add(int k) //同上
{
res=res-(ll)(num[a[k]]*num[a[k]]);
num[a[k]]++;
res=res+(ll)(num[a[k]]*num[a[k]]);
}
int main()
{
while(scanf("%d%d",&n,&m)!=EOF)
{
sz=sqrt(n);
res=0;
memset(num,0,sizeof(num));
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
pos[i]=i/sz;
}
for(int i=1;i<=m;i++)
{
scanf("%d%d",&qu[i].l,&qu[i].r);
qu[i].id=i;
}
L=1,R=0,res=0;
sort(qu+1,qu+1+m,cmp);
for(int i=1;i<=m;i++) //莫队算法
{
while(L<qu[i].l)
{
del(L);
L++;
}
while(L>qu[i].l)
{
L--;
add(L);
}
while(R<qu[i].r)
{
R++;
add(R);
}
while(R>qu[i].r)
{
del(R);
R--;
}
int idx=qu[i].id;
ans[idx].l=res-(R-L+1);
ans[idx].r=(ll)(qu[i].r-qu[i].l+1)*(qu[i].r-qu[i].l);
ll kk=gcd(ans[idx].l,ans[idx].r);
ans[idx].l/=kk;ans[idx].r/=kk;
}
for(int i=1;i<=m;i++)
printf("%lld/%lld\n",ans[i].l,ans[i].r);
}
}