HDU 4638 Group (莫队算法||线段树离散查询)

题目地址:HDU 4638

先写了一发莫队,莫队可以水过。很简单的莫队,不多说。

代码如下:

#include <iostream>
#include <string.h>
#include <math.h>
#include <queue>
#include <algorithm>
#include <stdlib.h>
#include <map>
#include <set>
#include <stdio.h>
#include <time.h>
using namespace std;
#define LL long long
#define pi acos(-1.0)
#pragma comment(linker, "/STACK:1024000000")
const int mod=1e9+7;
const int INF=0x3f3f3f3f;
const double eqs=1e-9;
const int MAXN=100000+10;
int a[MAXN];
LL ha[MAXN], ans[MAXN], res;
struct node {
        int l, r, id, pos;
} fei[MAXN];
bool cmp(node x, node y)
{
        return x.pos<y.pos||x.pos==y.pos&&x.r<y.r;
}
void reduce(int tmp)
{
        if(ha[tmp-1]&&ha[tmp+1]) res++;
        else if(!ha[tmp-1]&&!ha[tmp+1]) res--;
        ha[tmp]=0;
}
void add(int tmp)
{
        if(ha[tmp-1]&&ha[tmp+1]) res--;
        else if(!ha[tmp-1]&&!ha[tmp+1]) res++;
        ha[tmp]=1;
}
int main()
{
        int t, n, m, i, j, k, tmp, l, r;
        scanf("%d",&t);
        while(t--) {
                scanf("%d%d",&n,&m);
                for(i=1; i<=n; i++) {
                        scanf("%d",&a[i]);
                }
                k=sqrt(n*1.0);
                for(i=0; i<m; i++) {
                        scanf("%d%d",&fei[i].l,&fei[i].r);
                        fei[i].id=i;
                        fei[i].pos=fei[i].l/k;
                }
                sort(fei,fei+m,cmp);
                memset(ha,0,sizeof(ha));
                l=1;
                r=0;
                res=0;
                for(i=0; i<m; i++) {
                        while(r>fei[i].r) {
                                tmp=a[r];
                                reduce(tmp);
                                r--;
                        }
                        while(r<fei[i].r) {
                                r++;
                                tmp=a[r];
                                add(tmp);
                        }
                        while(l<fei[i].l) {
                                tmp=a[l];
                                reduce(tmp);
                                l++;
                        }
                        while(l>fei[i].l) {
                                l--;
                                tmp=a[l];
                                add(tmp);
                        }
                        ans[fei[i].id]=res;
                }
                for(i=0; i<m; i++) {
                        printf("%I64d\n",ans[i]);
                }
        }
        return 0;
}

然后又看了解题报告,发现线段树+离线查询也可以过。而且感觉很神奇的做法。。

首先先离线保存下来。然后从左向右维护,维护的是前面的值对于当前枚举值的相对个数。对于当前第i个数来说,先将这个数的值更新为1,代表一个独立的串,然后找a[i]-1和a[i]+1前面存在不存在,如果存在的话,则说明前面的这两个数已经不能作为独立的串了,相对于a[i]来说,可以共存在a[i]的串中,所以就要将a[i]-1和a[i]+1分别-1.然后再在r值为i的询问中,直接线段树求和就可以了。代码如下:

#include <iostream>
#include <string.h>
#include <math.h>
#include <queue>
#include <algorithm>
#include <stdlib.h>
#include <map>
#include <set>
#include <stdio.h>
#include <time.h>
using namespace std;
#define LL long long
#define pi acos(-1.0)
#pragma comment(linker, "/STACK:1024000000")
#define root 1, n, 1
#define lson l, mid, rt<<1
#define rson mid+1, r, rt<<1|1
const int mod=1e9+7;
const int INF=0x3f3f3f3f;
const double eqs=1e-9;
const int MAXN=100000+10;
int a[MAXN], pos[MAXN], sum[MAXN<<2], ans[MAXN];
struct node
{
        int l, r, id;
}fei[MAXN];
bool cmp(node x, node y)
{
        return x.r<y.r;
}
void PushUp(int rt)
{
        sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}
void Update(int p, int x, int l, int r, int rt)
{
        if(l==r){
                sum[rt]+=x;
                return ;
        }
        int mid=l+r>>1;
        if(p<=mid) Update(p,x,lson);
        else Update(p,x,rson);
        PushUp(rt);
}
int Query(int ll, int rr, int l, int r, int rt)
{
        if(ll<=l&&rr>=r){
                return sum[rt];
        }
        int mid=l+r>>1, ans=0;
        if(ll<=mid) ans+=Query(ll,rr,lson);
        if(rr>mid) ans+=Query(ll,rr,rson);
        return ans;
}
int main()
{
        int t, n, m, i, j;
        scanf("%d",&t);
        while(t--){
                scanf("%d%d",&n,&m);
                for(i=1;i<=n;i++){
                        scanf("%d",&a[i]);
                        pos[a[i]]=i;
                }
                for(i=0;i<m;i++){
                        scanf("%d%d",&fei[i].l,&fei[i].r);
                        fei[i].id=i;
                }
                memset(sum,0,sizeof(sum));
                sort(fei,fei+m,cmp);
                j=0;
                for(i=1;i<=n;i++){
                        Update(i,1,root);
                        if(a[i]>1&&pos[a[i]-1]<i) Update(pos[a[i]-1],-1,root);
                        if(a[i]<n&&pos[a[i]+1]<i) Update(pos[a[i]+1],-1,root);
                        while(j<m&&fei[j].r<=i){
                                ans[fei[j].id]=Query(fei[j].l,i,root);
                                j++;
                        }
                }
                for(i=0;i<m;i++){
                        printf("%d\n",ans[i]);
                }
        }
        return 0;
}

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-08-01 16:14:05

HDU 4638 Group (莫队算法||线段树离散查询)的相关文章

hdu 4638 Group(莫队算法|离线线段树)

Group Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 1323    Accepted Submission(s): 703 Problem Description There are n men ,every man has an ID(1..n).their ID is unique. Whose ID is i and i-

hdu 4638 Group 莫队算法

题目链接 很裸的莫队, 就不多说了... 1 #include<bits/stdc++.h> 2 using namespace std; 3 #define pb(x) push_back(x) 4 #define ll long long 5 #define mk(x, y) make_pair(x, y) 6 #define lson l, m, rt<<1 7 #define mem(a) memset(a, 0, sizeof(a)) 8 #define rson m+1

[莫队算法 线段树 斐波那契 暴力] Codeforces 633H Fibonacci-ish II

题目大意:给出一个长度为n的数列a. 对于一个询问lj和rj.将a[lj]到a[rj]从小到大排序后并去重.设得到的新数列为b,长度为k,求F1*b1+F2*b2+F3*b3+...+Fk*bk.当中F为斐波那契数列.F1=F2=1.对每一个询问输出答案模m. 区间查询离线 用莫队算法 开棵权值线段树,然后用斐波那契的性质update F(n+m)=F(n+1)*F(m)+F(n)*F(m-1); #include<cstdio> #include<cstdlib> #includ

HDU 4630 No Pain No Game (线段树离线查询)

题目地址:HDU 4630 这题一看数据范围,于是一直在思考n*logn的算法..实在没想到好方法,找了找题解,发现都是用的n*sqrt(n)*logn的方法...算了算,这个复杂度的确可以过..好吧.. 然后就可以先离线下来将询问按r值排序,然后枚举每个数,并且用sqrt(n)的方法枚举所有的约数,然后对于每个约数,对最近的一次出现的这个约数的地方进行更新.因为对于当前区间来讲,只要最近的这个地方更新了,那么前面的查询肯定都能查到这个地方的最大值,所以前面的不用更新. 代码如下: #inclu

[补档计划] 树6 - 莫队算法

[CF633H] Fibonacci-ish II 题意 给定长度为 $N$ 个序列 $A = (a_1, a_2, ..., a_N)$ . $M$ 组询问 $(l, r)$ : 将 $a_l, a_{l+1}, ..., a_r$ 提取出来, 排序, 去重, 得到长度为 $K$ 的序列 $B = (b_1, b_2, ..., b_K)$ , 求 $\sum_{i = 1}^K f_ib_i$ . 其中 $f_i$ 为斐波那契数列的第 $i$ 项: $f_0 = f_1 = 1, f_i =

BZOJ 2589 Spoj 10707 Count on a tree II 强制在线莫队算法(TLE)

题目大意:给定一棵树,每个节点有一个颜色,多次询问某条路径上颜色数量,强制在线 正解是块状数组,强制在线莫队会TLE到死,想AC这道题的不用看了 如果朴素的跑树上莫队其实并不难- - 但是强制在线 因此我们可以考虑强制在线莫队算法 将树分成O(n^1/3)块,每块大小O(n^2/3) 记录每两块之间的答案.每种颜色的出现次数和哪些点被记录到了答案中 每次查询先找到两端点所在块的端点的答案,然后暴力用莫队转移即可 空间复杂度O(n^1/3)*O(n^1/3)*O(n)=O(n^5/3) 预处理时间

HDU 6278 - Just h-index - [莫队算法+树状数组+二分][2018JSCPC江苏省赛C题]

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6278 Time Limit: 6000/3000 MS (Java/Others) Memory Limit: 132768/132768 K (Java/Others) Problem Description The h-index of an author is the largest h where he has at least h papers with citations not les

hdu 5273 Dylans loves sequence(区间逆序对数-莫队算法)

n<=1000,q<=100000,求区间内逆序对数,从[l,r]显然可以log(n)的时间内移动到[l-1,r],[l+1,r],[l,r-1],[l,r+1],那么就可以用莫队进行离线 复杂度大概是O(n*sqrt(n)*log2(n)),不过可以暴力枚举起点,然后向后统计,然后O(1)回答,不过n再大就无法解决了,这个即使是n<=1e5也可以很快得到答案,开-o优化,1e6也可以很快得到答案 #include<bits/stdc++.h> using namespace

BZOJ 1878 SDOI2009 HH的项链 树状数组/莫队算法

题目大意:给定一个序列.求一个区间内有多少个不同的数 正解是树状数组 将全部区间依照左端点排序 然后每次仅仅统计左端点開始的每种颜色的第一个数即可了 用树状数组维护 我写的是莫队算法 莫队明显能搞 m√m明显慢了点可是还是能接受的一个复杂度 一開始离散化数组开小了各种秒RE-- 跪了 #include<cmath> #include<cstdio> #include<cstring> #include<iostream> #include<algori