• 洛谷 P1494 [国家集训队]小Z的袜子 /【模板】莫队


    题目链接

    0x00 思路

    我们考虑统计区间内相同颜色的袜子的个数,设(S_i)表示第(i)种颜色的袜子数,则这种颜色袜子对方案数的贡献就是(S_i * (S_i - 1))

    长度为(S)的区间,其总方案数为(S * (S - 1))

    最后答案约分即可,如果概率为0,输出0/1

    0x01 莫队维护

    我们只要维护区间的相同颜色袜子数,存在一个(cnt)数组里,同时维护可行方案数(sum)

    每次用两个指针(L R),不断左右移动,去和查询区间重合即可

    部分代码:

        while(L > q[i].l) -- L,sum += cnt[a[L]] << 1,++ cnt[a[L]];
        while(L < q[i].l) -- cnt[a[L]],sum -= cnt[a[L]] << 1,++ L;
        while(R > q[i].r) -- cnt[a[R]],sum -= cnt[a[R]] << 1,-- R;
        while(R < q[i].r) ++ R,sum += cnt[a[R]] << 1,++ cnt[a[R]];
    
    

    注意维护的顺序

    对于询问区间,我们将其排序,排序原则

    1. 将左端点分块,块大小为(sqrt(n)),记左端点所在的块的编号为(pos)

    2. 如果两个查询区间(pos)不同,则按(pos)从小到大排序

    3. 如果(pos)相同,则按右端点(r)从小到大排序

    部分代码:

    bool cmp(node a,node b){
        return (a.pos ^ b.pos) ? (a.pos < b.pos) : (a.r < b.r);
    }
    

    复杂度证明 参考博客 莫队算法——从入门到黑题

    1. 右端点的移动:在每一块里复杂度最坏是(O(n))的,总共有(O(sqrt{n}))块,复杂度(O(n sqrt{n}))

    2. 左端点的移动:在每一块里有(X_i)个左端点,左端点的移动最坏是(O(X_isqrt{n}))的;从前一块跳到后一块,最坏是(O(2 sqrt{n}))的距离,也就是说左端点每次跳跃都有可能是(O(sqrt{n}))级别的,复杂度(O(n sqrt{n}))

    3. 排序,复杂度(O(n log{n}))

    总复杂度是(O(nsqrt{n} ))

    0x02 Code

    #include<bits/stdc++.h>
    using namespace std;
    #define N 50005
    int read(){
    	int x=0; char c=getchar(); int flag=1;
    	while(!isdigit(c)) { if(c=='-') flag=-1; c=getchar(); }
    	while(isdigit(c)) { x=((x+(x<<2))<<1)+(c^48); c=getchar(); }
    	return x*flag;
    }
    int n,m,a[N],cnt[N];
    struct node{ int l,r,pos,id; } q[N]; 
    bool cmp(node a,node b){
        return (a.pos ^ b.pos) ? (a.pos < b.pos) : ( (a.pos & 1) ? (a.r < b.r) : (a.r > b.r) );
    }
    long long ans[N][2];
    long long gcd(long long x,long long y) { return !y ? x : gcd(y,x % y); }
    signed main(){
        n = read(),m = read();
        int size = sqrt(n);
        for(int i = 1;i <= n;i ++) a[i] = read();
        for(int i = 1;i <= m;i ++) q[i].l = read(),q[i].r = read(),q[i].pos = (q[i].l - 1) / size + 1, q[i].id = i;
        sort(q + 1,q + m + 1,cmp);
        long long sum=0; int L=1,R=0;
        for(int i = 1;i <= m;i ++){
    	    while(L > q[i].l) -- L,sum += cnt[a[L]] << 1,++ cnt[a[L]];
    		while(L < q[i].l) -- cnt[a[L]],sum -= cnt[a[L]] << 1,++ L;
    		while(R > q[i].r) -- cnt[a[R]],sum -= cnt[a[R]] << 1,-- R;
    		while(R < q[i].r) ++ R,sum += cnt[a[R]] << 1,++ cnt[a[R]];
    		
    		ans[q[i].id][0] = sum; ans[q[i].id][1] = 1ll * (R - L + 1) * (R - L);
    	}
    	for(int i = 1;i <= m;i ++){
    	    if(ans[i][0] == 0) { puts("0/1"); continue; }
    		long long z = gcd(ans[i][0],ans[i][1]);
    	    ans[i][0] /= z; ans[i][1] /= z;
    	    printf("%lld/%lld
    ",ans[i][0],ans[i][1]);
    	    
    	}
    	return 0;
    }
    
    
  • 相关阅读:
    Ansible facts
    K8S基础概念
    Ansible CMDB
    Docker 核心技术与实现原理
    如何在 Centos7 中使用阿里云的yum源
    一篇文全面了解DevOps:从概念、关键问题、兴起到实现需求
    关于操作stream流
    service not available now, maybe disk full, CL: 0.95 CQ: 0.95 INDEX: 0.95, maybe your broker mach
    AOP的底层实现,动态代理和cglib代理
    模拟浏览器加载静态资源
  • 原文地址:https://www.cnblogs.com/zzhzzh123/p/12240435.html
Copyright © 2020-2023  润新知