1188 - Fast Queries (莫队算法)

1188 - Fast Queries
    PDF (English)    Statistics    Forum
Time Limit: 3 second(s)    Memory Limit: 64 MB
Given an array of N integers indexed from 1 toN, and q queries, each in the form i j, you have to findthe number of distinct integers from index i to j (inclusive).

Input
Input starts with an integer T (≤ 5),denoting the number of test cases.

The first line of a case is a blank line. The next linecontains two integers N (1 ≤ N ≤ 105), q (1≤ q ≤ 50000). The next line contains N space separatedintegers forming the array. There integers range in [0, 105].

Each of the next q lines will contain a query whichis in the form i j (1 ≤ i ≤ j ≤ N).

Output
For each test case, print the case number in a single line.Then for each query you have to print a line containing number of distinctintegers from index i to j.

Sample Input
Output for Sample Input
1

 

8 5

1 1 1 2 3 5 1 2

1 8

2 3

3 6

4 5

4 8

Case 1:

4

1

4

2

4

Note
Dataset is huge. Use faster I/O methods.

题意:n个数,m个区间,找区间不同数的个数

题解: 裸的莫队分块,时间复杂度近似为q*(n^1.5).


 

#include<cstdio>
#include<iostream>
#include<fstream>
#include<algorithm>
#include<functional>
#include<cstring>
#include<string>
#include<cstdlib>
#include<iomanip>
#include<numeric>
#include<cctype>
#include<cmath>
#include<ctime>
#include<queue>
#include<stack>
#include<list>
#include<set>
#include<map>
using namespace std;
const int maxn=1000005;
typedef long long ll;
int n,m,k;
//莫队算法主要处理离线问题,查询只给出L,R
//当[L,R]很容易向[L-1,R],[L+1,R],[L,R-1],[L,R+1]转移时可用莫队
//注意转移的时候先扩张再收缩,L先向右,L再向左,最后再收缩
//add就是当前区间添加某元素时要做的操作
//del就是当前区间删除某元素时要做的操作
//add,del函数写的时候都要注意结构顺序
struct node
{
    int l,r,id;
}Q[maxn]; ///保存询问值
int pos[maxn];///保存所在块
bool cmp(const node &a,const node &b)///先按l所在块的大小排,在按r的位置排
{
   return pos[a.l]<pos[b.l]||(pos[a.l]==pos[b.l]&&(pos[a.l]&1?a.r<b.r:a.r>b.r));///奇偶排序
}
ll gcd_(ll a,ll b)
{
	return b==0?a:gcd_(b,a%b);
}
int num[maxn*2];
int a[maxn];

ll ans[maxn],ans2[maxn];
int L=0,R=0;
ll Ans=0;
void add(int x)
{
	num[a[x]]++; 
	if(num[a[x]]==1) Ans++;
}
void del(int x)
{
	
	 num[a[x]]--;
	 if(num[a[x]]==0) Ans--;
}
int main()
{      int T;
    scanf("%d",&T);
    int cas=0;
    while(T--)
    {
	memset(num,0,sizeof(num));
	cas++;
	scanf("%d%d",&n,&m);
	int sz=n/sqrt(m*2/3);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        pos[i]=i/sz;
    }
    
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&Q[i].l,&Q[i].r);
        Q[i].id=i;
    }
    sort(Q+1,Q+1+m,cmp);
	num[0]=0;
    L=1;R=0;Ans=0;
    for(int i=1;i<=m;i++) 
    {
        while(R<Q[i].r)
        {
            R++;
            add(R);
        }
        while(L>Q[i].l) ///前缀和L+1>Q[i].l
        {
            L--;
            add(L);
        }
        while(L<Q[i].l)///前缀和L+1<Q[i].l
        {
            del(L);
            L++;
        }
        while(R>Q[i].r)
        {
            del(R);
            R--;
        }
        ans[Q[i].id]=Ans;
    }
    printf("Case %d:\n",cas);
     for(int i=1;i<=m;i++)
        printf("%lld\n",ans[i]);
    }
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值