题目描述
此时己是凌晨两点,刚刚做了 Codeforces 的小 A 掏出了英语试卷。英语作业其实不算多,一个小时刚好可以做完。然后是一个小时可以做完的数学作业,接下来是分别都是一个小时可以做完的化学,物理,语文…小A压力巨大。
这是小 A 碰见了一道非常恶心的数学题,给定了一个长度为
n
n
n 的数列和若干个询问,每个询问是关于数列的区间
[
l
,
r
]
[l,r]
[l,r](表示数列的第
l
l
l 个数到第
r
r
r 个数),首先你要统计该区间内大于等于
a
a
a,小于等于
b
b
b 的数的个数,其次是所有大于等于
a
a
a,小于等于
b
b
b 的,且在该区间中出现过的数值的个数。
小 A 望着那数万的数据规模几乎绝望,只能向大神您求救,请您帮帮他吧。
N
=
100000
N=100000
N=100000,
M
=
1000000
M=1000000
M=1000000。
算法分析
莫队算法+分块,和 Gty的二逼妹子序列 差不多,是那个题的简单增强版。
BZOJ 上过了,LG 上 RE 了两个点,不改了。(╯‵□′)╯︵┻━┻
代码实现
#include <cstdio>
#include <cmath>
#include <algorithm>
#define cmin(x,y) x=std::min(x,y)
#define cmax(x,y) x=std::max(x,y)
const int maxn
=100005;
const int maxm
=1000005;
struct ask
{int id
,l
,r
,a
,b
;} qs
[maxm
];
int sz
,bl
[maxn
],le
[maxn
],ri
[maxn
];
inline bool cmp(const ask
&x
,const ask
&y
) {return bl
[x
.l
]^bl
[y
.l
]?x
.l
<y
.l
:bl
[x
.l
]&1?x
.r
<y
.r
:x
.r
>y
.r
;}
int a
[maxn
],cnt
[maxn
],num
[maxn
],sum
[maxn
],ans1
[maxm
],ans2
[maxm
];
int main() {
int n
,m
;scanf("%d%d",&n
,&m
);
for(register int i
=1;i
<=n
;++i
) scanf("%d",&a
[i
]);
for(register int i
=0;i
<m
;++i
) {qs
[i
].id
=i
;scanf("%d%d%d%d",&qs
[i
].l
,&qs
[i
].r
,&qs
[i
].a
,&qs
[i
].b
);}
sz
=n
/sqrt(m
*2/3);for(register int i
=1;i
<=n
;++i
) {bl
[i
]=(i
-1)/sz
+1;le
[i
]=n
;ri
[i
]=0;}
std
::sort(qs
,qs
+m
,cmp
);
for(register int i
=1;i
<=n
;++i
) {cmin(le
[bl
[i
]],i
);cmax(ri
[bl
[i
]],i
);}
for(register int i
=0,l
=1,r
=0;i
<m
;++i
) {
while(l
<qs
[i
].l
) {
--cnt
[a
[l
]];--sum
[bl
[a
[l
]]];
if(!cnt
[a
[l
]]) --num
[bl
[a
[l
]]];
++l
;
}
while(l
>qs
[i
].l
) {
--l
;
if(!cnt
[a
[l
]]) ++num
[bl
[a
[l
]]];
++cnt
[a
[l
]];++sum
[bl
[a
[l
]]];
}
while(r
<qs
[i
].r
) {
++r
;
if(!cnt
[a
[r
]]) ++num
[bl
[a
[r
]]];
++cnt
[a
[r
]];++sum
[bl
[a
[r
]]];
}
while(r
>qs
[i
].r
) {
--cnt
[a
[r
]];--sum
[bl
[a
[r
]]];
if(!cnt
[a
[r
]]) --num
[bl
[a
[r
]]];
--r
;
}
int &res1
=ans1
[qs
[i
].id
]=0,&res2
=ans2
[qs
[i
].id
]=0;int a
=qs
[i
].a
,b
=qs
[i
].b
;
if(bl
[a
]^bl
[b
]) {
for(register int i
=a
;i
<=ri
[bl
[a
]];++i
) {res1
+=cnt
[i
];if(cnt
[i
]) ++res2
;}
for(register int i
=bl
[a
]+1;i
<=bl
[b
]-1;++i
) {res1
+=sum
[i
];res2
+=num
[i
];}
for(register int i
=le
[bl
[b
]];i
<=b
;++i
) {res1
+=cnt
[i
];if(cnt
[i
]) ++res2
;}
}
else for(register int i
=a
;i
<=b
;++i
) {res1
+=cnt
[i
];if(cnt
[i
]) ++res2
;}
}
for(register int i
=0;i
<m
;++i
) printf("%d %d\n",ans1
[i
],ans2
[i
]);
return 0;
}