战场原黑仪的一维收藏柜
时间限制: 1 s
空间限制: 256000 KB
题目等级 : 大师 Master
题目描述
听过化物语op —-staple stable (建议去听)的人都知道战场原很喜欢订书机。
事实上她喜欢所有金属物品。
于是显然她们家有个收藏柜,是一维的。
有一天她邀你去她家参观。
当你刚进了她家门她突然把门锁上,从里面打不开,说你如果不能帮她统计出正确的数值就要和她在一起或者被温柔的订书机XXX了。
这时候你想打电话给妈妈回家。
发现手机被她的吸铁石吸走了。
她的收藏柜每格都放置了某种刀具(0<=种类代号<=10^9,不同格放置的种类可能相同),共n格。
设W(x,y)表示x~y格中所有刀具所属种类形成的集合。
她会发出若干条(x,y,z)的询问( 1<=x<=y<z<=n ),你需要输出一个数值表示 |W(x,y)∩W(y+1,z)| ,即同时出现在第x~y和第y+1~z格中的刀具种数。
输入描述
第一行两个数n, Q(Q为询问数)
接下来一行,n个数,第i个数a[i]表示第i格放置的刀具种类。
接下来Q行,每行三个数x,y,z,表示一组询问
输出描述
对于每个询问输出一行表示答案。
样例输入
5 2
1 1 3 3 1
1 3 5
2 4 5
样例输出
2
1
数据范围及提示
对于30%数据 1<=n,Q<=1000
对于100%数据, 1<=n,Q<=200000,0<=种类代号<=109 ,输入都是整数
研究了几天,终于想通了。先吐槽一下题面,话说我当年补物语系列的时候,其中几个女主中我最讨厌的就是战场原,现在又折磨我几天,愈加讨厌。
关于这道题,最暴力的算法就是对于每一条询问遍历并比较两个区间,时间复杂度 O(qN2)
看起来挺简单,然而20W数据,呵呵~
如何优化呢?
主要有两个方面:
- 最容易想到的就是在线转离线,这样就可以减掉一个Q的级别了,于是 O(N2)
- 然而对与20W数据还是太慢,很容易想到开一个数组,储存在左区间是否存在过,这样就可以降到 O(N) 了。然而,题目最后还有一行字挡在我们成功的道路上,“ 种类代号<=109 ”,呵呵,开布尔数组的话也就953MB,我现在想杀了战场原,啊不,是出题人。不过我不能就这样放弃,毕竟已经想了这么久,既然不让我们开hash数组,那么就退而求此次用Treap代替,这里使用map比较方便
代码挺短,不解释
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
using namespace std;
map<int,int> mp;
const int MAXN=2e5,MAXQ=2e5;
int n,Q;
int a[MAXN+1],lastPos[MAXN+1];
struct FT
{
int na[MAXN+1];
int lowbit(int x) {return x&(-x);}
void add(int x,int y) {for(;x<=n;x+=lowbit(x)) na[x]+=y;}
int calSum(int x) {int res=0;for(;x;x-=lowbit(x)) res+=na[x];return res;}
} ft;
struct R
{
int l,r;
int qid,flag;
} range[3*MAXN+1];int rcnt;
bool cmp(const R & a,const R & b){return a.r<b.r;}
int ans[MAXQ+1];
int main()
{
freopen("hitagi.in","r",stdin);
freopen("hitagi.out","w",stdout);
int i;
scanf("%d%d",&n,&Q);
for(i=1;i<=n;i++)
{
scanf("%d",&a[i]);
lastPos[i]=mp[a[i]];
mp[a[i]]=i;
}
for(i=1;i<=Q;i++)
{
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
range[++rcnt]=(R){x,y,i,-1};
range[++rcnt]=(R){y+1,z,i,-1};
range[++rcnt]=(R){x,z,i,1};
}
sort(range+1,range+rcnt+1,cmp);
int j=1,k=0;
for(i=1;i<=n;i++)
{
if(lastPos[i])
{
ft.add(lastPos[i],1);
++k;
}
for(;j<=rcnt&&range[j].r==i;j++)
ans[range[j].qid]+=(k-ft.calSum(range[j].l-1))*range[j].flag;
}
for(i=1;i<=Q;i++)
printf("%d\n",ans[i]);
return 0;
}