HYSBZ 2038 莫队算法

题意

中文题,不解释

题解

莫队入门题。其实莫队就是暴力,只不过利用了上一次计算的结果,同时对区间进行合理分块,可以使得复杂度可以降低到O(N^(3/2))。题目要求求区间内任意选两个数,相同的概率。如果时间宽松一点,对于稍微有点数学基础的选手都可以在5分钟内写出相应代码,组合数公式随便搞一下就可以。
对于这道题就不行了,不但不能暴力,并且用什么数据结构都会感觉不舒服(会各种树套树的大牛可以无视此句。)不过我们可以用莫队算法优化一下暴力流程,我们可以将整个数轴分为M块,每块的大小为sqrt(MX))(MX为数轴最大值)然后我们可以进行排序,在同一块的元素放在一起。
至于分块的原因其实很简单,如果直接排序的话,很容易被卡。比如说(100,1),(50,1000),(1,1)这样一个样例。由于莫队是利用上一次结果进行转移,那么这个样例就需要先从1转移到1000,然后再从1000转移到1。明显可以看出来这样是不好的。所以我们可以允许X在一定范围内非单增,比如我们可以把一块设置为100,那么在这一块内便根据Y排序,那么便会排列成(100,1),(1,1),(50,1000),这样的话转移变少了1000次,优化很明显。
解决了分快的问题,莫队算法的核心基本就解决了。另外的话,莫队算法还有一个核心就是,对于每一个查询,不直接计算,而是通过上一个状态转移的这一个状态来计算值,这样分块的作用才能达到。

代码

#include <iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#include<cmath>
#include<queue>
#include<string>
#include<set>
#include<map>
#include<bitset>
#include<stack>
#include<string>
#define UP(i,l,h) for(int i=l;i<h;i++)
#define DOWN(i,h,l) for(int i=h-1;i>=l;i--)
#define W(a) while(a)
#define MEM(a,b) memset(a,b,sizeof(a))
#define LL long long
#define INF 0x3f3f3f3f3f3f3f3f
#define MAXN 50010
#define MOD 1000000007
#define EPS 1e-3
#define int LL
using namespace std;
struct Node{
    int l,r,id,val,all;
};
int c[MAXN],pos[MAXN],cnt[MAXN],val;
Node nodes[MAXN];
int n,m;

bool cmp(Node a,Node b){
    return pos[a.l]==pos[b.l]?a.r<b.r:a.l<b.l;
}

bool cmp1(Node a,Node b){
    return a.id<b.id;
}

void update(int p,int add){
    val-=cnt[p]*(cnt[p]-1)/2;
    cnt[p]+=add;
    val+=cnt[p]*(cnt[p]-1)/2;;
}

void solve(){
    MEM(cnt,0);
    int l=1,r=1;
    val=0;
    update(c[1],1);
    UP(i,0,m){
        nodes[i].all=(nodes[i].r-nodes[i].l+1)*(nodes[i].r-nodes[i].l)/2;
        for(;l<nodes[i].l;l++) update(c[l],-1);
        for(;l>nodes[i].l;l--) update(c[l-1],1);
        for(;r>nodes[i].r;r--) update(c[r],-1);
        for(;r<nodes[i].r;r++) update(c[r+1],1);
        nodes[i].val=val;
    }
}

main() {
    scanf("%lld%lld",&n,&m);
    UP(i,1,n+1) scanf("%lld",&c[i]);
    int mx=0;
    UP(i,0,m){
        scanf("%lld%lld",&nodes[i].l,&nodes[i].r);
        nodes[i].id=i;
        mx=max(mx,nodes[i].l);
    }
    int q=sqrt(mx);
    UP(i,1,mx+1) pos[i]=i/q;
    sort(nodes,nodes+m,cmp);
    solve();
    sort(nodes,nodes+m,cmp1);
    UP(i,0,m){
        int g=__gcd(nodes[i].val,nodes[i].all);
        if(g==0) printf("0/1\n");
        else printf("%lld/%lld\n",nodes[i].val/g,nodes[i].all/g);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值