莫队+分块

来源:互联网 发布:微信 for mac 2.0 编辑:程序博客网 时间:2024/06/05 16:29

这个题是bzoj-2038

题目链接:

题意:给出n个数字,m次询问,每次询问在区间\([l_i,r_i]\)之间任选两个数字相等的概率是多少。(n,q<=50000)


思路:这道题是比较模板的莫队分块了,对于一个区间询问[L,R],我们要求的ans是 

∑C(f[i],2)/C(r-l+1,2)

然后有:∑(f[i]^2-f[i])/((r-l+1)*(r-l))

其中sum(f[i])指第i种颜色在[L,R]中出现的次数 ,sum(f[i])就等于区间长度r-l+1,
那么我们现在求出各个询问区间中sum(f[i])2就行了,
注意: 
1.当一种颜色数量ci增加1时,我们可以看出ans=ansf[i]
*f[i]+(f[i]+1)2
,        简化可得ans=ans+(f[i]2+1),同样减少1时,ans-=f[i]2     2+(f[i]1)2,简化得ans=ans+(f[i]21),这样做的好处是减少乘法且可用位运算,优化常数(然而并没有什么卵用) 
2.极限数据50000*50000如果不用longlong会教你做人←_← 

莫队算法可用于解决一类可离线且在得到区间\([l,r]\)的答案后,能在\(O(1)\)\(O(\log_2{n})\)得到区间\([l,r+1]\)\([l-1,r]\)的答案的问题,这个区间是m个不变的区间,

莫队的分块写法是很好的一个东西,持续学习中


代码:

#include <iostream>#include <cstdio>#include <cstdlib>#include <cmath>#include <cstring>#include <string>#include <algorithm>#include <set>#include <map>#include <stack>#include <vector>#include <queue>#define ri(n) scanf("%d",&n)#define oi(n) printf("%d\n",n)#define rl(n) scanf("%lld",&n)#define ol(n) printf("%lld\n",n)#define rep(i,l,r) for(i=l;i<=r;i++)#define rep1(i,l,r) for(i=l;i<r;i++)using namespace std;typedef long long ll;const int inf=0x3f3f3f3f;const int maxn=50000+500;const int epg=10-8;int n,m;int len;ll ans;int bb[maxn],a[maxn],f[maxn];ll gcd(ll a,ll b){    if(!b)        return a;    return gcd(b,a%b);}struct node{    int l,r,id;    ll a,b;    bool operator<(const node &x)const    {        return bb[l]<bb[x.l]||(bb[l]==bb[x.l]&&r<x.r);    }    void _gcd()    {        ll k=gcd(a,b);        a/=k;        b/=k;    }}q[maxn];/*void modit(int pos,int add){    ans+=2*add*f[a[pos]]+1;    f[a[pos]]+=add;}*/bool cmp(node x,node y){    return x.id<y.id;}void solve(){    int l=1,r=0;    ans=0;    for(int i=1;i<=m;i++)    {        if(q[i].l<l)        {            for(l=l-1;q[i].l<l;l--)            {                ans-=f[a[l]]*f[a[l]];                f[a[l]]++;                ans+=f[a[l]]*f[a[l]];            }            ans-=f[a[l]]*f[a[l]];            f[a[l]]++;            ans+=f[a[l]]*f[a[l]];                 //modit(l,1);            //modit(l,1);        }        if(l<q[i].l)        {            for(;l<q[i].l;l++)            {                ans-=f[a[l]]*f[a[l]];                f[a[l]]--;                ans+=f[a[l]]*f[a[l]];            }                //modit(l,-1);        }        if(q[i].r<r)        {            for(;q[i].r<r;r--)            {                ans-=f[a[r]]*f[a[r]];                f[a[r]]--;                ans+=f[a[r]]*f[a[r]];            }                //modit(r,-1);        }        if(r<q[i].r)        {            for(r=r+1;r<q[i].r;r++)            {                ans-=f[a[r]]*f[a[r]];                f[a[r]]++;                ans+=f[a[r]]*f[a[r]];            }            ans-=f[a[r]]*f[a[r]];            f[a[r]]++;            ans+=f[a[r]]*f[a[r]];                //modit(r,1);            //modit(r,1);        }        if(q[i].l==q[i].r)        {            q[i].a=0;            q[i].b=1;            continue;        }        q[i].a=ans-(q[i].r-q[i].l+1);        q[i].b=ll((q[i].r-q[i].l+1)*(q[i].r-q[i].l));        q[i]._gcd();        //printf("%lld/%lld\n",q[i].a,q[i].b);    }    sort(q+1,q+1+m,cmp);    for(int i=1;i<=m;i++)        printf("%lld/%lld\n",q[i].a,q[i].b);}int main(){    while(scanf("%d%d",&n,&m)==2)    {        memset(bb,0,sizeof(bb));        memset(f,0,sizeof(f));        len=sqrt(n);        for(int i=1;i<=n;i++)            bb[i]=(i-1)/len+1;        for(int i=1;i<=n;i++)            scanf("%d",&a[i]);        for(int i=1;i<=m;i++)        {            scanf("%d%d",&q[i].l,&q[i].r);            q[i].id=i;        }        sort(q+1,q+1+m);        solve();    }    return 0;}/*6 41 2 3 3 3 22 61 33 51 6*/


原创粉丝点击