bzoj3809: Gty的二逼妹子序列

Description

Autumn和Bakser又在研究Gty的妹子序列了!但他们遇到了一个难题。
 
对于一段妹子们,他们想让你帮忙求出这之内美丽度∈[a,b]的妹子的美丽度的种类数。
 
为了方便,我们规定妹子们的美丽度全都在[1,n]中。
 
给定一个长度为n(1<=n<=100000)的正整数序列s(1<=si<=n),对于m(1<=m<=1000000)次询问“l,r,a,b”,每次输出sl...sr中,权值∈[a,b]的权值的种类数。

 

Input

第一行包括两个整数n,m(1<=n<=100000,1<=m<=1000000),表示数列s中的元素数和询问数。
 
第二行包括n个整数s1...sn(1<=si<=n)。
 
接下来m行,每行包括4个整数l,r,a,b(1<=l<=r<=n,1<=a<=b<=n),意义见题目描述。
 
保证涉及的所有数在C++的int内。
 
保证输入合法。

 

Output

对每个询问,单独输出一行,表示sl...sr中权值∈[a,b]的权值的种类数。

 

Sample Input

10 10
4 4 5 1 4 1 5 1 2 1
5 9 1 2
3 4 7 9
4 4 2 5
2 3 4 7
5 10 4 4
3 9 1 1
1 4 5 9
8 9 3 3
2 2 1 6
8 9 1 4

Sample Output

2
0
0
2
1
1
1
0
1
2

HINT

 

样例的部分解释:

 

5 9 1 2

子序列为4 1 5 1 2

在[1,2]里的权值有1,1,2,有2种,因此答案为2。

 

3 4 7 9

子序列为5 1

在[7,9]里的权值有5,有1种,因此答案为1。

 

4 4 2 5

子序列为1

没有权值在[2,5]中的,因此答案为0。

 

2 3 4 7

子序列为4 5

权值在[4,7]中的有4,5,因此答案为2。

 

建议使用输入/输出优化。
 
这题第一眼看上去是莫队+树状数组,但复杂度是O(M√N log N),会TLE
正确的姿势是对权值分块,这样使得每次查询复杂度变为O(√n),而修改的复杂度变为O(1)
这样总复杂度为O(M√N)
code:
 1 #include<cstdio>
 2 #include<iostream>
 3 #include<cmath>
 4 #include<cstring>
 5 #include<algorithm>
 6 #define maxn 100005
 7 using namespace std;
 8 char ch;
 9 int n,m,siz,s[maxn],cnt[maxn],sum[400],pos[maxn],ans[maxn*10];
10 bool ok;
11 void read(int &x){
12     for (ok=0,ch=getchar();!isdigit(ch);ch=getchar()) if (ch=='-') ok=1;
13     for (x=0;isdigit(ch);x=x*10+ch-'0',ch=getchar());
14     if (ok) x=-x;
15 }
16 struct Query{
17     int l,r,a,b,id;
18     void init(int i){read(l),read(r),read(a),read(b),id=i;}
19 }list[maxn*10];
20 bool cmp(Query a,Query b){
21     if (pos[a.l]!=pos[b.l]) return pos[a.l]<pos[b.l];
22     return pos[a.r]<pos[b.r];    
23 }
24 void add(int col){
25     cnt[col]++;
26     if (cnt[col]==1) sum[pos[col]]++;
27 }
28 void del(int col){
29     cnt[col]--;
30     if (!cnt[col]) sum[pos[col]]--;
31 }
32 int query(int l,int r){
33     int ans=0;
34     for (int i=pos[l]+1;i<pos[r];i++) ans+=sum[i];
35     if (pos[l]!=pos[r]){
36         for (int i=l;pos[i]==pos[l];i++) if (cnt[i]) ans++;
37         for (int i=r;pos[i]==pos[r];i--) if (cnt[i]) ans++;
38     }
39     else for (int i=l;i<=r;i++) if (cnt[i]) ans++;
40     return ans;
41 }
42 void work(){
43     int l=1,r=0;
44     for (int i=1;i<=m;i++){
45         for (;r<list[i].r;r++) add(s[r+1]);
46         for (;r>list[i].r;r--) del(s[r]);
47         for (;l<list[i].l;l++) del(s[l]);
48         for (;l>list[i].l;l--) add(s[l-1]);
49         ans[list[i].id]=query(list[i].a,list[i].b);
50     }
51 }
52 int len;
53 char ss[30];
54 void write(int v){
55     if (!v) putchar('0');
56     for (len=0;v;v/=10) ss[++len]=(v%10)+'0';
57     for (;len;len--) putchar(ss[len]);
58     putchar('\n');
59 }
60 int main(){
61     read(n),read(m),siz=(int)sqrt(n);
62     for (int i=1;i<=n;i++) read(s[i]);
63     for (int i=1;i<=m;i++) list[i].init(i);
64     for (int i=1;i<=n;i++) pos[i]=i/siz;
65     sort(list+1,list+m+1,cmp);
66     work();
67     for (int i=1;i<=m;i++) write(ans[i]);
68     return 0;
69 }

 

转载于:https://www.cnblogs.com/chenyushuo/p/4708397.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值