• 【BZOJ2038】小Z的袜子(2009国家集训队)-莫队算法


    测试地址:小Z的袜子

    做法:设f(i)为颜色i在区间[l,r]内出现的次数,则区间[l,r]的答案为:ΣC(2,f(i))/C(2,r-l+1),面对这种东西线段树就无能为力了......怎么办呢?

    于是本人今天学习了传说中离线处理区间询问的无敌算法——莫队算法,感觉妙极!莫队算法的讲解见这里。这篇博客中也以这一题作为例题,讲得比较详细了,那我就来讲讲具体实现吧:因为答案的式子中大部分都可以直接通过l和r求出,所以我们只用维护一个ans=Σ(f(i))^2,在从(l,r)转移到(l,r+1)时,假设点r+1的颜色是c,那么ans就比原来多:(f(c)+1)^2-f(c)^2=2*f(c)+1。而从(l,r)转移到(l,r-1)时就略微不同,ans比原来多:(f(c)-1)^2-f(c)^2=-2*f(c)+1。于是可以发现,当我们转移的区间是扩大的时候,ans增加的就是2*f(c)+1,反之就是-2*f(c)+1,然后答案就按照上面那篇博客说的一样计算即可。化成最简分数其实就是分子和分母同除它们的gcd(这个还不懂的话......听天由命吧)。

    以下是本人代码:

    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    #include <cmath>
    using namespace std;
    int n,m,c[50010],pos[50010],f[50010]={0};
    struct query
    {
      long long a,b;
      int l,r,id;
    }q[50010];
    
    bool cmp1(query a,query b)
    {
      return pos[a.l]<pos[b.l]||(pos[a.l]==pos[b.l]&&a.r<b.r);
    }
    
    bool cmp2(query a,query b)
    {
      return a.id<b.id;
    }
    
    void init()
    {
      scanf("%d%d",&n,&m);
      int block=(int)sqrt((double)n+0.5);
      for(int i=1;i<=n;i++)
      {
        scanf("%d",&c[i]);
        pos[i]=(i-1)/block+1;
      }
      for(int i=1;i<=m;i++)
      {
        scanf("%d%d",&q[i].l,&q[i].r);
    	q[i].id=i;
      }
      sort(q+1,q+m+1,cmp1);
    }
    
    void transfer(long long p,long long &ans,long long add)
    {
      ans=ans+2*add*f[c[p]]+1;
      f[c[p]]+=add;
    }
    
    long long gcd(long long a,long long b)
    {
      return (b==0)?a:gcd(b,a%b);
    }
    
    void solve()
    {
      long long ans=0,l=1,r=0;
      for(int i=1;i<=m;i++)
      {
        if (r<q[i].r) while(r<q[i].r) r++,transfer(r,ans,1);
    	if (q[i].l<l) while(l>q[i].l) l--,transfer(l,ans,1);
    	if (r>q[i].r) while(r>q[i].r) transfer(r,ans,-1),r--;
    	if (q[i].l>l) while(l<q[i].l) transfer(l,ans,-1),l++;
    	if (l==r) {q[i].a=0,q[i].b=1;continue;}
    	q[i].a=ans-(r-l+1);q[i].b=(r-l+1)*(r-l);
        long long g=gcd(q[i].a,q[i].b);
    	q[i].a/=g,q[i].b/=g;
      }
      sort(q+1,q+m+1,cmp2);
      for(int i=1;i<=m;i++)
        printf("%lld/%lld
    ",q[i].a,q[i].b);
    }
    
    int main()
    {
      init();
      solve();
      
      return 0;
    }
    


  • 相关阅读:
    计算直线的交点数
    不容易系列之(4)——考新郎
    神、上帝以及老天爷
    N!
    Number Sequence
    33_ABB机器人智能周期保养与复位操作
    34_WorldZone区域监控功能的使用
    三菱PLC(FX3U)的模拟量应用
    第19集 PLC盒子的使用
    第18集 使用黑盒设计创建宏文件
  • 原文地址:https://www.cnblogs.com/Maxwei-wzj/p/9793720.html
Copyright © 2020-2023  润新知