zoukankan      html  css  js  c++  java
  • 莫队-小Z的袜子

     ----普通莫队


    首先
    清楚概率怎么求
    假设我们要求从区间l到r中拿出一对袜子的概率
    sum[i]为第i种袜子在l到r中的数量

    $$frac{sum_{i=l}^{r} {[sum[i] imes (sum[i]-1)]}}{ (r-l+1) imes {(r-l)}}qquad$$

    转化一下可以得到

    $$frac{sum_{i=l}^{r} {sum[i]^{2}}-(r-l+1)}{ (r-l+1) imes {(r-l)}}qquad$$

    普通莫队是一种离线算法 并且充分利用上一个得到的答案来求得当前询问的答案


    怎么由上一个答案来得到当前的答案呢?


    $$ans=sum_{i=l}^{r} {sum[i]^{2}}$$

    即分母的一部分(减去(r-l+1)即可得到分母)

    现在要求[l+1,r]这个区间的答案
    若第l只袜子的编号为x 则只有sum[x]减少了1
    更新ans的操作如下:
    ans-=sum[x]*sum[x];
    sum[x]-=1;
    ans+=sum[x]*sum[x]
    即:减去从前对答案的贡献 加上现在的贡献
    求[l-1,r],[l,r-1],[l,r+1]的做法类比可得
    整理一下可以得到change函数

    //x为新增加或减少的点
    //若为新增加的点 如l指针左移和r指针右移 则w=1
    //反之 w=-1
    /*例子:求[l-1,r] change(l,-1)
           求[l,r+1] change(r+1,1) ……*/
    change(int x,int w)
    {
        ans-=sum[x]*sum[x];sum[x]+=w;ans+=sum[x]*sum[x];
    }

     

    为了使l和r指针尽可能少的移动(优化时间)
    我们需要给所有的问题的l和r排序

    构造cmp函数
    要用分块


    将整个长度为n的序列分为sqrt(n)块
    cmp为:若l与r在同一块中 则按照l从小到大排序
          否则 按照r从小到大排序
    这样l指针每次最多跳2*(n/sqrt(n))次
    最后求gcd 即可
    还要记得l==r的特判


    一种优秀的gcd求法

    //普通gcd
    int gcd(int x,int y)
    {
      return y==0?x:gcd(y,x%y);
    }


    其实可以看成是把x赋值为x%y 再调换x,y的位置 求gcd(x,y)
    假设我们要调换a,b的值

    //普通做法:
    int tmp;tmp=a;a=b;b=tmp;
    
    //其实可以利用位运算  异或
    x^=y;y^=x;x^=y;
    
    /*  第一步:x=x^y
        第二步:y=y^(x^y) 由于y^y=0 所以 y=x
        第三步:x=(x^y)^x 同理可得 x=y
    */

    可以得到gcd函数

    ll gcd(ll a,ll b)
    {
      while(b^=a^=b^=a%=b);return a;
    }
    //从右至左运算

    分析over

    code
    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cmath>
    #define ll long long
    #define yes(i,a,b) for(register int i=a;i<=b;i++)
    #define M 50010
    using namespace std;
    int n,m,tot,len;
    ll ans,f_ans[M][2],sum[M];
    int c[M],be[M];
    struct node {int l,r,id;} q[M];
    bool cmp(node x,node y)
    {
      if(be[x.l]==be[y.l]) return x.r<y.r;
      return x.l<y.l;
    }
    void change(int x,int w)
    {
      ans-=(ll)(sum[c[x]]*sum[c[x]]);sum[c[x]]+=w;ans+=(ll)(sum[c[x]]*sum[c[x]]);
    }
    ll gcd(ll a,ll b)
    {
      while(b^=a^=b^=a%=b);return a;
    }
    int main()
    {
      //freopen("1.in","r",stdin);
      //freopen("1.out","w",stdout);
      scanf("%d%d",&n,&m);
      len=sqrt(n);
      yes(i,1,n) scanf("%d",&c[i]),be[i]=i/len+1;
      yes(i,1,m) scanf("%d%d",&q[i].l,&q[i].r),q[i].id=i;
      sort(q+1,q+m+1,cmp);
      int l=1,r=0;
      yes(i,1,m)
        {
          while(l<q[i].l) change(l,-1),l++;
          while(l>q[i].l) change(l-1,1),l--;
          while(r<q[i].r) change(r+1,1),r++;
          while(r>q[i].r) change(r,-1),r--;
          ll ans1,ans2;
          ans1=ans-(ll)(q[i].r-q[i].l+1);ans2=(ll)(q[i].r-q[i].l+1)*(q[i].r-q[i].l);
          if(ans1==0) ans2=1;
          else
        {
          ll g=gcd(ans2,ans1);
          ans1/=g;ans2/=g;
        }
          f_ans[q[i].id][0]=ans1;f_ans[q[i].id][1]=ans2;
        }
      yes(i,1,m) printf("%lld/%lld
    ",f_ans[i][0],f_ans[i][1]);
      return 0;
    }
    光伴随的阴影
  • 相关阅读:
    R 多图间距调整
    ggplot2 颜色渐变(离散颜色)设置
    R语言因子排序
    利用plink软件基于LD信息过滤SNP
    利用vcftools比较两个vcf文件
    在R语言中使用Stringr进行字符串操作
    perl 数组快速去除重复元素
    Shell中 ##%% 操作变量名
    Java基础之数值类型之间的转换
    Java中0.2减0.1 结果为什么不是0.1?
  • 原文地址:https://www.cnblogs.com/forward777/p/10086599.html
Copyright © 2011-2022 走看看