HDU-5213,洛谷-P1494 (莫队算法)

题目链接: 小Z的袜子

思路

在这里插入图片描述
莫队算法用于解决离线处理问题,即先把所有询问记录一遍,对询问的进行排序操作后,最终一起输出答案。对于区间询问,其实我们就是设置了两个指针L,R,我们其实可以在O(1)的时间内完成从【L,R】的询问到【L+1,R】或者是【L,R+1】的询问,因此排序的原因就是使得指针跳来跳去的次数能够减少一些。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
struct node{
    ll l,r,A,B,id;
}q[50001];
ll col[50001],sum[50001],ans,a[50001],n,m;
ll cmp(node a,node b)
{
    return col[a.l]==col[b.l]?(a.r<b.r):(a.l<b.l);
}
ll cmp1(node a,node b)
{
    return a.id < b.id;
}
ll update(ll x,ll add)
{
    ans -= sum[a[x]]*sum[a[x]];
    sum[a[x]] += add;
    ans += sum[a[x]]*sum[a[x]];
}
ll gcd(ll x,ll y)
{
    return y==0?x:gcd(y,x%y);
}
int main()
{
    ll i;
    cin >> n >> m;
    for(i = 1  ;i <= n ; i++)
    {
        cin >> a[i];
        col[i] = i/int(sqrt(n))+1;
    }
    for(i = 1; i <= m ; i++)
    {
        cin >> q[i].l >> q[i].r;
        q[i].id = i;
    }
    sort(q+1,q+m+1,cmp);
    int l = 1 ,r = 0;
    for(i = 1;i <= m ; i++)
    {
        while(l < q[i].l) {update(l,-1),l++;}
        while(l > q[i].l) {update(l-1,1),l--;}
        while(r < q[i].r) {update(r+1,1),r++;}
        while(r > q[i].r)  {update(r,-1),r--;}
        if(l == r) {q[i].A =0 ;q[i].B =1;continue;}
        q[i].A = ans - (q[i].r-q[i].l+1);
        q[i].B = (q[i].r - q[i].l + 1)*(q[i].r - q[i].l);
        ll g = gcd(q[i].A,q[i].B);
        q[i].A = q[i].A /g;
        q[i].B = q[i].B /g;
    }
    sort(q+1,q+m+1,cmp1);
    for(i = 1; i <= m ;i++)
    {
        printf("%lld/%lld\n",q[i].A,q[i].B);
    }
    return 0;
}

题目链接 HDU-5213

思路

首先我们要知道莫队算法在求的时候需要一段连续的区间,然后再这段连续的区间中求题目要求的,比如说这道题就是求区间内两个数的和为K的对数,但是这道题是给你两段区间让你求对数,所以这个时候我们应该先用容斥原理来处理一下。
在这里插入图片描述
然后我们就可以分别求每个区间上的对数在进行求和了。


#include <bits/stdc++.h>
using namespace std;
const int N = 30001 * 4;
struct node{
    int l,r,id,f;
}q[N];
int num[N],sum,tot,a[N],ans[N],Be[N],k;
void add(int l,int r,int id,int f)
{
    q[++tot] = node{l,r,id,f};
}
int cmp(node A,node B)
{
    return Be[A.l] == Be[B.l]?A.r<B.r:A.l<B.l;
}
void update(int x,int d)
{
    if(d > 0)
    {
        if(k >= x)
        sum += num[k-x];
        num[x]++;
    }
    else
    {
        num[x]--;
        if(k >= x)
        sum -= num[k-x];
    }
}
int main()
{
    cin.tie(0);
    ios::sync_with_stdio(0);
    int n,m,i,block,l,r,u,v;
    while(cin >> n >> k)
    {
    tot = sum = 0;
    block = (int)sqrt(n);
    memset(num,0,sizeof(num));
    memset(ans,0,sizeof(ans));
    for(i = 1 ;i <= n ; i++)
    {
        cin >> a[i];
        Be[i] = i/block + 1;
    }
    cin >> m;
    for(i = 1;i <= m ; i++)
    {
        cin >> l >> r >> u >> v;
        add(l,v,i,1);
        add(l,u-1,i,-1);
        add(r+1,v,i,-1);
        add(r+1,u-1,i,1);
    }
    sort(q+1,q+tot+1,cmp);
    int l = 1,r = 0;
    for(i = 1;i <= tot ; i++)
    {
        while(l < q[i].l) {update(a[l++],-1);}
        while(l > q[i].l) {update(a[--l],1);}
        while(r < q[i].r)  {update(a[++r],1);}
        while(r > q[i].r)  {update(a[r--],-1);}
        ans[q[i].id] += q[i].f*sum;
    }
    for(i = 1 ;i <= m ;i++)
    {
        cout << ans[i] << endl;
    }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值