hdu5212 Code 莫队算法

这道题需要一些莫队算法的知识
定义记号f(A,B)表示询问区间A,B时的答案
用记号+表示集合的并
利用莫队算法我们可以计算出任意f(A,A)的值
不妨假设A=[l1,r1],B=[l2,r2],C=[r1+1,l2?1]
容易知道f(A,B)=f(A+B+C,A+B+C)+f(C,C)?f(A+C,A+C)?f(C+B,C+B)
因此一个询问被拆成四个可以用莫队算法做的询问
总的时间复杂度为O(msqrt(n))
(以上是官方题解)
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<map>
using namespace std;
typedef long long LL;
const int N = 30000*4 + 10;

int pos[N];
struct pp{
    int l,r,id;
    int ans;
}p[N];
int cmp(pp a,pp b){
    if(pos[a.l]==pos[b.l]) return a.r < b.r;
    return a.l < b.l;
}
int cmp2(pp a,pp b){
    return a.id < b.id;
}

struct que{
    int l1,l2,r1,r2;
}q[N];
int block,n,k,m,num;
int a[N],cnt[N],answer;
LL x;

map<LL,int> mm;

void update(int x,int v){
    int val = k - a[x];
    if(val <= 0) return ;
    answer += cnt[val]*v;
    cnt[a[x]] += v;
}

void solve(){
    int l,r;
    answer = 0;
    for(int i=1,l=1,r=0;i<=num;i++){//按块进行更新
        for(;r<p[i].r;r++)
            update(r+1,1);
        for(;r>p[i].r;r--)
            update(r,-1);
        for(;l<p[i].l;l++)
            update(l,-1);
        for(;l>p[i].l;l--)
            update(l-1,1);
        p[i].ans = answer;
    }
}

int main(){
    while(scanf("%d",&n)!=EOF){
        mm.clear();
        num = 0;
        block = (int)sqrt(n)+1;
        for(int i=1;i<=n;i++) pos[i] = i/block + 1;
        scanf("%d",&k);
        memset(cnt,0,sizeof(cnt));
        for(int i=1;i<=n;i++)
            scanf("%d",&a[i]);
        scanf("%d",&m);
        for(int i=1;i<=m;i++){
            int l1,l2,r1,r2;
            scanf("%d%d%d%d",&l1,&r1,&l2,&r2);
            q[i].l1 = l1 , q[i].r1 = r1 , q[i].l2 = l2 , q[i].r2 = r2;
            p[++num].l = l1 , p[num].r = l2-1 ;
            x = l1*40000+(l2-1);
            mm[x] = num;
            p[num].id = num;

            p[++num].l = r1+1 , p[num].r = r2 ;
            x = (r1+1)*40000+r2;
            mm[x] = num;
            p[num].id = num;

            if(l2-1 >= r1+1){
                p[++num].l = r1+1 , p[num].r = l2-1 ;
                x = (r1+1)*40000+(l2-1);
                mm[x] = num;
                p[num].id = num;
            }

            p[++num].l = l1 , p[num].r = r2 ;
            x = l1*40000+r2;
            mm[x] = num;
            p[num].id = num;
        }
        sort(p+1,p+1+num,cmp);
        solve();
        sort(p+1,p+1+num,cmp2);
        for(int i=1;i<=m;i++){
            int l1,l2,r1,r2;
            l1 = q[i].l1 , l2 = q[i].l2 , r1 = q[i].r1 , r2 = q[i].r2 ;
            LL ans = 0;

            x = l1*40000+r2;
            ans += p[mm[x]].ans;

            if(l2-1>=r1+1){
                x = (r1+1)*40000+(l2-1);
                ans += p[mm[x]].ans;
            }

            x = (r1+1)*40000+r2;
            ans -= p[mm[x]].ans;

            x = l1*40000+(l2-1);
            ans -= p[mm[x]].ans;

            printf("%lld\n",ans);
        }
    }
    return 0;
}
时间: 2024-10-16 12:39:32

hdu5212 Code 莫队算法的相关文章

莫队算法小结(Markdown版)

wtf,最近挖坑有点小多啊,没办法>_<容我先把糖果公园A了再来写这个吧= =看看今天能不能A掉 好吧,我承认我第二天才把糖果公园A掉>_<下面把这篇小结补上 首先众所周知的是莫队算法是要把询问先按左端点属于的块排序,再按右端点排序 复杂度就先不证了,有兴趣的同学可以自己YY下或者查阅资料 下面举几个例子详细说明 1.小Z的袜子 Description: 给定一个序列m个询问 每次询问: 区间中选两个数,两个数相等的概率 若概率为0则输出01 仔细观察发现,令x表示x这个值出现的次

[voj 1551]E - Pairs 2014年武汉大学邀请赛E题 莫队算法

题目大意 有n个数,m个查询,对于每个查询,询问指定区间,有多少个数对的绝对值小于等于2. 解题思路 莫队O^1.5 首先将询问离线处理左端点进行编号,每sqrt(n)个为一组 sort结构体 当左端点编号相同时,比较右端点大小.小的放在前面. 对于每组询问暴力处理,只需处理当前新加入(删除的数字在当前区间内有多少点和它的绝对值只差小于2即可) 唯一要注意的是加点是先更新答案再计数,删点是先计数器-1再更新答案 因为对于每个询问,左端点的修改量不超过sqrt(n) 右端点每一组最坏的复杂度是修改

kyeremal-bzoj2038-[2009国家集训队]-小z的袜子(hose)-莫队算法

bzoj2038-[2009国家集训队]-小z的袜子(hose) F.A.Qs Home Discuss ProblemSet Status Ranklist Contest ModifyUser  Manacher Logout 捐赠本站 Notice:省选季快乐&另求历年World Final数据,谢谢&OJ试题突破3000大关! 2038: [2009国家集训队]小Z的袜子(hose) Time Limit: 20 Sec  Memory Limit: 259 MB Submit: 

BZOJ 3236 AHOI 2013 作业 莫队算法

题目大意:给出一些数,问在一个区间中不同的数值有多少种,和在一个区间中不同的数值有多少个. 思路:由于没有修改,所以就想到了莫队算法.然后我写了5K+的曼哈顿距离最小生成树,然后果断T了.(100s的时限啊,刷status都要刷疯了..,结果最后加了手写读入也没能A).后来果断放弃,写了分块版的莫队算法.84sAC...这题卡的..貌似莫队并不是正解. 其实用分块来写莫队就很简单了.只需要将所有询问的区间排序,左端点所在块作为第一键值,右端点作为第二季键值排序,之后就可以转移了.理论上来时还是曼

BZOJ 2038 小Z的袜子(莫队算法)

莫队算法如果我们已知[l,r]的答案,能在O(1)时间得到[l+1,r]的答案以及[l,r-1]的答案,即可使用莫队算法.时间复杂度为O(n^1.5).如果只能在logn的时间移动区间,则时间复杂度是O(n^1.5*log n).其实就是找一个数据结构支持插入.删除时维护当前答案. 这道题的话我们很容易用数组来实现,做到O(1)的从[l,r]转移到[l,r+1]与[l+1,r]. 那么莫队算法怎么做呢?以下都是在转移为O(1)的基础下讨论的时间复杂度.另外由于n与m同阶,就统一写n.如果已知[l

莫队算法小结

唔,想有更加舒爽的阅读体验请移步http://mlz000.logdown.com/posts/252433-mo-algorithm-summary 首先众所周知的是莫队算法是要把询问先按左端点属于的块排序,再按右端点排序 复杂度就先不证了,有兴趣的同学可以自己YY下或者查阅资料 下面举几个例子详细说明 1.小Z的袜子 Description: 给定一个序列m询问 每次询问: 区间中选两个数,两个数相等的概率 若概率为则输出0/1 仔细观察发现,令x表示x个值出现的次数,则每次询问[l,r]区

【莫队算法】【权值分块】poj2104 K-th Number / poj2761 Feed the dogs

先用莫队算法保证在询问之间转移的复杂度,每次转移都需要进行O(sqrt(m))次插入和删除,权值分块的插入/删除是O(1)的. 然后询问的时候用权值分块查询区间k小值,每次是O(sqrt(n))的. 所以总共的复杂度是O(m*(sqrt(n)+sqrt(m)))的. 常数极小. 别的按权值维护的数据结构无法做到O(1)地插入删除. poj2104 的输出优化 别忘了处理负数. 完爆主席树,这份代码目前在 poj2761 上 Rank1. Rank Run ID User Memory Time

BZOJ 2038 2009国家集训队 小Z的袜子 莫队算法

题目大意:给出一些袜子的排列顺序,每次问一段区间中有多少相同颜色的袜子对. 思路:莫队算法真是一个神奇的算法.首先,暴力枚举是O(n^2)的时间复杂度,这肯定是不行的.假如区间是保证不重合的,那么就可以将总的时间转移的复杂度降到O(n).很遗憾,题目中没有这个保证.于是乎,神秘的莫队就发明了一种神奇的算法. 对于每一个询问,我们将它看成一个平面上的点(x1,y1),同样的也就会有其他的点分布在平面中.假如还有一个点(x2,y2),那么我们从第一个区间转移到第二个区间需要改变的元素总数为|x1 -

【BZOJ-3052】糖果公园 树上带修莫队算法

3052: [wc2013]糖果公园 Time Limit: 200 Sec  Memory Limit: 512 MBSubmit: 883  Solved: 419[Submit][Status][Discuss] Description Input Output Sample Input Sample Output 84 131 27 84 HINT Source Solution 树上带修莫队 本质还是树上莫队,详情可以转 BZOJ-3757苹果树 但是这里需要修改,就需要一些特殊的地方