HDU 3333 Turing Tree 题解(线段树+离线操作)
题目链接
题目大意
给你一个长度为\(n(n\le30000)\)的数组\(a\)
给你\(m(m\le100000)\)次查询,求出区间\([l,r]\)中出现过的数字之和(出现过多次只算一次)
题目思路
这个乍一看很像线段树,其实也是线段树
但是你会发现你根本不好去维护这个线段树
你发现没这个数组没有修改操作,都是查询操作
那么对于这种题目就应该要想到离线操作
把所有查询按照\(r\)升序排序
然后保证\([1,r]\)中若出现相同的数,则这个数只在\([1,r]\)的最右边记录
那么这样显然可以保证答案的正确性,只要有一个\(last\)数组维护每个元素前的相同元素的最近值即可
由于\(a[i]\)达到\(10^9\)用\(map\)即可
代码
#include<bits/stdc++.h>
#define fi first
#define se second
#define debug cout<<"I AM HERE"<<endl;
using namespace std;
typedef long long ll;
const int maxn=1e5+5,inf=0x3f3f3f3f,mod=1e9+7;
const double eps=1e-6;
int n,m;
int a[maxn];
ll ans[maxn];
ll tree[maxn<<2];
struct node{
int l,r,id;
}nd[maxn];
bool cmp(node a,node b){
return a.r<b.r;
}
ll query(int node,int l,int r,int ql,int qr){
//这里没有被建树,显然没有值
if(ql<=l&&r<=qr){
return tree[node];
}
int mid=(l+r)/2;
ll sum=0;
if(mid>=ql) sum+=query(node<<1,l,mid,ql,qr);
if(mid<qr) sum+=query(node<<1|1,mid+1,r,ql,qr);
return sum;
}
void update(int node,int l,int r,int pos,int val){
if(l==r){
tree[node]=val;
return ;
}
int mid=(l+r)/2;
if(mid>=pos) update(node<<1,l,mid,pos,val);
else update(node<<1|1,mid+1,r,pos,val);
tree[node]=tree[node<<1]+tree[node<<1|1];
}
signed main(){
int _;scanf("%d",&_);
while(_--){
map<int,int>last;
memset(tree,0,sizeof(tree));
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
scanf("%d",&m);
for(int i=1;i<=m;i++){
scanf("%d%d",&nd[i].l,&nd[i].r);
nd[i].id=i;
}
sort(nd+1,nd+1+m,cmp);
int pos=1;
for(int i=1;i<=m;i++){
while(pos<=nd[i].r){
if(last[a[pos]]){
update(1,1,n,last[a[pos]],0);
}
update(1,1,n,pos,a[pos]);
last[a[pos]]=pos;
pos++;
}
ans[nd[i].id]=query(1,1,n,nd[i].l,nd[i].r);
}
for(int i=1;i<=m;i++){
printf("%lld\n",ans[i]);
}
}
return 0;
}
不摆烂了,写题