bzoj 2038 小Z的袜子 莫队模板

n个袜子每个都有一个颜色,求他给的区间 取两个袜子正好同色的概率
复杂度nsqrt(n);
先分sqrt(n)块 然后把区间按最左边界所在的块排序,如果所在块相等按右边届从小到大排序。
定义此时的l,r,ans(起始为1,0,0),然后枚举区间改变l,r,ans

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define LL long long
#define MAXN 50010
using namespace std;
int n,m,unit;
int num[MAXN],co[MAXN];
struct node{
       int l,r,i;
}no[51000];
LL gcd(LL a,LL b)
{
   while(b)
   {
       LL t = b;
       b = a%b;
       a = t; 
   }
   return a;
}
class node2{
    public: 
       LL a,b;
       inline void gao()
       {
            LL t = gcd(a,b);
            a/=t;
            b/=t;   
       }    
}ans1[MAXN];
inline bool cmp(node no1,node no2)//排序
{
     if(no1.l/unit!=no2.l/unit)return no1.l<no2.l;
     return no1.r<no2.r;
}
inline void work()//莫队
{
     int l = 1,r = 0;
     LL ans = 0;
     for(int i=0;i<m;i++)
     {
         while(r>no[i].r)
         {
             ans-=(LL)num[co[r]]*(num[co[r]]-1);
             num[co[r]]--;
             ans+=(LL)num[co[r]]*(num[co[r]]-1);    
             r--;
         }
         while(r<no[i].r)
         {
             r++;
             ans-=(LL)num[co[r]]*(num[co[r]]-1);
             num[co[r]]++;
             ans+=(LL)num[co[r]]*(num[co[r]]-1);    
         }
         while(l<no[i].l)
         {
             ans-=(LL)num[co[l]]*(num[co[l]]-1);
             num[co[l]]--;
             ans+=(LL)num[co[l]]*(num[co[l]]-1);
             l++;   
         }
         while(l>no[i].l)
         {
             l--;
             ans-=(LL)num[co[l]]*(num[co[l]]-1);
             num[co[l]]++;
             ans+=(LL)num[co[l]]*(num[co[l]]-1);    
         }
         ans1[no[i].i].a = ans;
         ans1[no[i].i].b = ((LL)no[i].r-no[i].l+1)*(no[i].r-no[i].l);
         ans1[no[i].i].gao();
     }
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
        scanf("%d",&co[i]);
    for(int i=0;i<m;i++)
    {
        scanf("%d%d",&no[i].l,&no[i].r);
        no[i].i = i;
    }
    unit = sqrt(m+0.001);
    sort(no,no+m,cmp);
    work();
    for(int i=0;i<m;i++)
        printf("%lld/%lld\n",ans1[i].a,ans1[i].b);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值