hdu 5213 Lucky && 莫队算法的理解

题意:

Lucky

 
 Accepts: 34
 
 Submissions: 267
 Time Limit: 6000/3000 MS (Java/Others)
 
 Memory Limit: 65536/65536 K (Java/Others)
问题描述
wld有n个数(a1...an)
保证对于任意
   
   
    
    1in1ain
   
   
wld有一个常数k保证
   
   
    
    2k2n
   
   
为了消除歧义保证k为奇数
他有
   
   
    
    m
   
   个询问
每个询问有参数
   
   
    
    l1,r1,l2,r2
   
   
保证
   
   
    
    (1l1r1<l2r2n)
   
   
对于每个询问你需要回答有多少个二元组
   
   
    
    (ij)
   
   满足:

   
   
    
    l1ir1
   
   
   
   
    
    l2jr2
   
   
   
   
    
    ai+aj=k
   
   
保证
   
   
    
    1n30000,1m30000
   
   
输入描述
多组数据(最多
   
   
    
    5
   
   组)
对于每组数据:
第一行:一个数
   
   
    
    n
   
   表示数的个数
接下来一行:一个数
   
   
    
    k
   
   表示wld的常数
接下来一行:
   
   
    
    n
   
   个数,依次为
   
   
    
    a1,a2,an
   
   
接下来一行:一个数
   
   
    
    m
   
   表示询问数
接下来
   
   
    
    m
   
   行:四个数
   
   
    
    l1,r1,l2,r2
   
   表示这组询问的参数
输出描述
对于每组数据:
对于每个询问输出二元组的数目
输入样例
5
3
1 2 1 2 3
1
1 2 3 5 
输出样例
2
Hint
a1 + a4 = 3
a2 + a3 = 3
题解:

1.一种神奇的离线分块法,貌似大家都叫它莫队算法

2.可以以nsqrt(n)的均摊复杂度处理区间查询(假设这个区间询问可以用区间长度的复杂度计算出来的话,且如果我已

经知道了A区间询问的信息,再询问包含A区间的B区间询问时,我可以不再重新计算A区间了,直接计算剩下的部分

就可以了)

3.方法是先将区间划分为sqrt(n)个sqrt(n)大小的区间

4.然后预处理:排序,先按左端点l所在的块号排序,块号相同的时候再按r的大小排序

5.这个图是对于区间询问的最坏情况


6.对于第一次询问,是枚举‘二’端点左边sqrt(n)的复杂度,再枚举‘二’端点右边sqrt(n)的复杂度

对于第二次询问,是枚举‘二’端点左边sqrt(n)的复杂度,再枚举‘二’端点右边一直到’三‘端点右边sqrt(n)的复杂度(因为

可以用到开始询问的区间1的右半部分)

7.这种最坏的询问方式,产生的复杂度是:比如有n次询问吧,每次询问都需要枚举某个端点左边sqrt(n),而对于某个

端点右边,也就是最多枚举整个大区间sqrt(n)次

8.最坏均摊复杂度就是nsqrt(n)

9.对于这个题目还需要用一下容斥原理,改成4个区间查询

总结:

1.舍友yyz大神还教我一种在线版的分块大法,回来试一试

2.觉着莫队算法很棒,暂时还是不能总结出个所以然来,不过随着知识体系的建立,应该会有一些新的感悟吧

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long LL;
#define MAXN 30005
#define MAXM 200
struct Node
{
    int l,r,id,add;
    bool operator < (const Node & c)const{
        return l / MAXM == c.l / MAXM ? r < c.r : l / MAXM < c.l / MAXM;
    }
}q[MAXN << 2];
int k,n,m,num[MAXN],has[MAXN],last[MAXN];
void solve()
{
    int mid,r,cur = -1;
    int ans1,ans2;
    for(int j = 0;j < m << 2;j++)
    {
        int ind = q[j].l / MAXM;
        if(ind != cur)
        {
            memset(has,0,sizeof(has));
            cur = mid;
            mid = (ind + 1) * MAXM , r = (ind + 1) * MAXM;
            ans2 = 0;
        }
        ans1 = 0;
        for(int i = r;i < q[j].r;i++)
        {
            if(k - num[i] <= n && 0 <= k - num[i])
                ans2 += has[k - num[i]];
            has[num[i]]++;
        }
        for(int i = q[j].l;i < min(q[j].r,mid);i++)
        {
            if(k - num[i] <= n && 0 <= k - num[i])
                ans1 += has[k - num[i]];
            has[num[i]]++;
        }
        for(int i = q[j].l;i < min(q[j].r,mid);i++)
            has[num[i]]--;
        //cout << ans << " ans " << endl;
        r = max(q[j].r,r);
        last[q[j].id] += q[j].add * (ans1 + ans2);
        cur = ind;
    }
}
int main()
{
    int l1,l2,r1,r2;
    while(scanf("%d%d",&n,&k) != EOF)
    {
        memset(last,0,sizeof(last));
        for(int i = 0;i < n;i++)
            scanf("%d",&num[i]);
        scanf("%d",&m);
        for(int i = 0;i < m;i++)
        {
            scanf("%d%d%d%d",&l1,&r1,&l2,&r2);
            l1--,l2--;
            q[i * 4].l = l1,q[i * 4].r = r2,q[i * 4].id = i,q[i * 4].add = 1;
            q[i * 4 + 1].l = l1,q[i * 4 + 1].r = l2,q[i * 4 + 1].id = i,q[i * 4 + 1].add = -1;
            q[i * 4 + 2].l = r1,q[i * 4 + 2].r = r2,q[i * 4 + 2].id = i,q[i * 4 + 2].add = -1;
            q[i * 4 + 3].l = r1,q[i * 4 + 3].r = l2,q[i * 4 + 3].id = i,q[i * 4 + 3].add = 1;
        }
        sort(q,q + m * 4);
        solve();
        for(int i = 0;i < m;i++)
            printf("%d\n",last[i]);
    }
}



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值