您的位置:首页 > 其它

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

2015-07-03 16:21 549 查看
题意:


Lucky

Accepts: 34

Submissions: 267

Time Limit: 6000/3000 MS (Java/Others)

Memory Limit: 65536/65536 K (Java/Others)

问题描述
wld有n个数(a1...an)
保证对于任意1≤i≤n,1≤ai≤n
wld有一个常数k保证2≤k≤2∗n
为了消除歧义保证k为奇数
他有m个询问
每个询问有参数l1,r1,l2,r2
保证(1≤l1≤r1<l2≤r2≤n)
对于每个询问你需要回答有多少个二元组(i,j)满足:
l1≤i≤r1且l2≤j≤r2且ai+aj=k
保证1≤n≤30000,1≤m≤30000

输入描述
多组数据(最多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]);
    }
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: