HDU 3874:http://acm.hdu.edu.cn/showproblem.php?pid=3874
题目大意: 区间不重复数求和——N个数,无序,M次查询。 查询是,给一个区间,要求出这个区间内 所有数字的和。 前提是:数字相同不能重复相加。。。。。。
N 最大50000, 查询的次数M 最大 200000,每个数最大为1000000,和肯定要用long long
看到众人都说此题简单,我非常为自己的愚蠢痛心疾首。这一题找错误样例就找了一百年啊一百年。以下我先弱弱的阐释一下自己的错误经历,若要看正确题解可以向后翻。
刚开始是 英语不好,自以为读懂了题意,以为是 将给的数组排序以后,求区间的不重复数字和。 开开心心的排序以后,感到十分奇怪,这样还要用树状数组?用一个数组sum来保存前n项和(包括当前 i ),一个数组repeat保存前n项中 出现的重复的数字和(包括当前 i )。给定一个区间 [ L , R ],用小学生等级的容斥求 sum[R] - sum[L] - repeat[R] + repeat[L-1] 即可,简直就是O(1)的复杂度啊。欢欣雀跃过了样例就交了,毫无悬念的WA。
痛定思痛,我摸着自己的头颅思考是错在哪了。看完一遍题意,哦题目没说要排序。呵呵,英语不行拉低智商。转念一想,无序的数组我这个方法也行的通啊。妙啊,(ノ`Д)ノ。于是改了一下,又交了已发,还是毫无悬念的WA。
痛定思痛定思痛,我抠着自己残存不多的头发,找错误样例。试了好几次都没什么毛病。感到绝望。断断续续找了一下午,诶,发现一个简单的样例就过不了: 2 1 2
如果使用这个3个数 2 1 2
那么理论上的前n项和为 2 3 5
前n项重复的数字是 0 0 2
选别的区间都没问题,但是如果选区间 【1,2】的话,(下标为0),则正确答案为3 ,可是按照 sum[R] - sum[L] - repeat[R] + repeat[L-1] 求出来就是 5-2-2+0=1 居然错了。
可恶,花了这么多精力,还是错误的。至少知道自己的思路为什么错了。如果用这个方法,是有可能会将不包含重复数字,但是前面确实有重复数字的区间给算错。。。。
看来还是自己蠢,还是用树状数组吧。
看了大家的博客,正确的题解是: 将给定的区间保存下来,离线处理,将它们以右区间小的在前排序。然后顺序解决每个排好序的区间即可。解决办法是,每次都更新记录重复的数字的最后出现的位置,然后将前面出现的重复数字的位置,在树状数组中 更新为0 。如此一来,求区间和的时候,相同的数字就可以只计算一次。
产生疑问1,为什么是按照右区间排序,不是左区间? ——因为按照右区间排序以后, 我们从下标1 开始更新数字出现的最后位置 操作 ,在更新到 右区间位置的时候, 这个查询就可以求出来了。
产生疑问2 ,对于每一个区间N ,都要从头扫一遍来更新区间 ,若区间都覆盖贼大为N ,那么 M次 操作总时间 为 查询次数*(扫的次数+求和时间) = M*(N+ logN) 肯定超时啊。—— 前一个区间,他们各个数字的最后出现位置的更新,对后面区间的更新操作已经重合了,不需要从头扫了。也就是说,扫的操作,就是N,所以总的复杂度是 MlogN +N
接下来就是映射了,用map也行自己写也行。
写完代码,怒交,居然TE了??难道是map的问题?不科学,看了别人代码也是用map怎么就我的TE呢?
把别人的代码一行一行扒下来惊醒的对照。。。。。。保存问题的数组开小了,不能用N的大小,要用M的大小,也就是N*4,尴尬,OJ居然给我TE报错。。。
心很痛,找了这么久BUG,此题真是收获良多。。。
下面就放一下代码吧,真是充满惆怅啊/(ㄒoㄒ)/
#include<bits/stdc++.h>
using namespace std;
#define inf 50009
#define INF 999999999
#define ll long long
#define loop(x,y,z) for(x=y;x<z;x++)
int n,a[inf];//输入数组
struct node
{
int id,l,r;
bool operator<(const node&j)const{return r<j.r;}
}ask[inf*4]; //询问的区间
ll ans[inf*4];//每次查询的答案
ll c[inf]; //树状数组
map<int,int>m;//映射数字最后出现位置
void init()
{
memset(c,0,sizeof c);
m.clear();
}
int lowbit(int i)
{
return i&-i;
}
ll cal_sum(int i)
{
ll sum=0;
while(i>0){
sum+=c[i];
i-=lowbit(i);
}
return sum;
}
void add(int i,int type) //type为0则抹除,为1则添加
{
int t=type?a[i]:-a[i];
while(i<=n){
c[i]+=t;
i+=lowbit(i);
}
}
int main()
{
int i,j,q,T;
scanf("%d",&T);
while(T--)
{
init();
scanf("%d",&n);
loop(i,1,n+1)scanf("%d",&a[i]);
scanf("%d",&q);
loop(i,1,q+1){ask[i].id=i;scanf("%d%d",&ask[i].l,&ask[i].r);}
sort(ask+1,ask+q+1);
j=1;
loop(i,1,q+1)
{
while(j<=ask[i].r)
{
if(m[a[j]]==0){m[a[j]]=j;add(j,1);}
else
{
add(m[a[j]],0);
add(j,1);
m[a[j]]=j;
}
j++;
}
ans[ask[i].id]=cal_sum(ask[i].r)-cal_sum(ask[i].l-1);
}
loop(i,1,q+1)printf("%lld\n",ans[i]);
}
return 0;
}