HDU 3333 Turing Tree 离线 线段树/树状数组 区间求和单点修改

题意:

  给一个数列,一些询问,问你$[l,r]$之间不同的数字之和

题解:

  11年多校的题,现在属于"人尽皆知傻逼题"

  核心思想在于:  对于一个询问$[x,R]$ 无论$x$是什么,整个数列中,对于答案有贡献的,只有每种数字中,$R$左边最近的一个

  对于数列$1,1,2,2,3,3,4,4,1,1,2,2,4,3,4,5,5,P...$和

           $0,0,0,0,0,0,0,0,0,1,0,2,0,3,4,0,5,P...$ 只要右边界保持在P-1,询问结果是等价的

具体操作就是,存储询问,按R排序,依次处理,删除当前询问R之前所有的,只保存最后一个

用线段树,树状数组都行,做单点修改区间查询就行..

bit

#include <bits/stdc++.h>
#define ll long long
#define IO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
#define pp pair<int,int>
#define rep(ii,a,b) for(int ii=a;ii<=b;ii++)
#define per(ii,a,b) for(int ii=a;ii>=b;ii--)
using namespace std;
const int maxn=1e6+10;
const int maxm=1e6*4+10;
const int INF=0x3f3f3f3f;
int casn,n,m,k;
#define lb(x) x&-x
ll bit[maxn],num[maxn];
void update(int pos,int x){
    while(pos<=n){
        bit[pos]+=x,pos+=lb(pos);
    }
}
ll sum(int pos){
    ll ans=0;
    while(pos){
        ans+=bit[pos],pos-=lb(pos);
    }
    return ans;
}
ll query(int a,int b){
    return sum(b)-sum(a-1);
}
struct node2 {
	int l,r,id;
}ask[maxn];
int cmp(node2 a,node2 b){
	return a.r<b.r;
}
map<ll,int>pos;
ll ans[maxn];
int main(){
//#define test
#ifdef test
  freopen("in.txt","r",stdin);freopen("out.txt","w",stdout);
#endif
	scanf("%d",&casn);
	while(casn--){
		scanf("%d",&n);
        rep(i,1,n) bit[i]=0;
		for(int i=1;i<=n;i++){
			scanf("%lld",num+i);
            update(i,num[i]);
		}
		pos.clear();
		scanf("%d",&k);
		int a,b;
		for(int i=1;i<=k;i++){
			scanf("%d%d",&a,&b);
			ask[i]=(node2){a,b,i};
		}
		sort(ask+1,ask+1+k,cmp);
		int r=1;
		for(int i=1;i<=k;i++){
			while(r<=ask[i].r){
				if(pos[num[r]]){
					update(pos[num[r]],-num[r]);
				}
				pos[num[r]]=r;
				r++;
			}
			ans[ask[i].id]=query(ask[i].l,ask[i].r);
		}
		for(int i=1;i<=k;i++){
			printf("%lld\n",ans[i]);
		}
	}

#ifdef test
  fclose(stdin);fclose(stdout);system("out.txt");
#endif
  return 0;
}

线段树

#include <bits/stdc++.h>
#define ll long long
#define IO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
#define pp pair<int,int>
#define rep(ii,a,b) for(int ii=a;ii<=b;ii++)
#define per(ii,a,b) for(int ii=a;ii>=b;ii--)
using namespace std;
const int maxn=1e5+10;
const int maxm=1e6+10;
const int INF=0x3f3f3f3f;
int casn,n,m,k;
#define nd lst[now]
struct node {
	int l,r;ll sum;
}lst[maxm];
ll num[maxn];
void maketree(int s=1,int t=n,int now=1){
	nd=(node){s,t,num[s]};
	if(s==t) return ;
	maketree(s,(s+t)>>1,now<<1);
	maketree(((s+t)>>1)+1,t,now<<1|1);
	nd.sum=lst[now<<1].sum+lst[now<<1|1].sum;
}
inline void pushup(int now) {
	nd.sum=lst[now<<1].sum+lst[now<<1|1].sum;
}
void update(int pos,int x,int now=1){
	if(pos<nd.l||pos>nd.r) return ;
	if(nd.r==nd.l){
		nd.sum=x;
		return;
	}
	update(pos,x,now<<1);
	update(pos,x,now<<1|1);
	pushup(now);
}
ll query(int s,int t,int now=1){
	if(s>nd.r||t<nd.l) return 0;
	if(s<=nd.l&&t>=nd.r) return nd.sum;
	return query(s,t,now<<1)+query(s,t,now<<1|1);
}
struct node2 {
	int l,r,id;
}ask[maxn];
int cmp(node2 a,node2 b){
	return a.r<b.r;
}
map<ll,int>pos;
ll ans[maxn];
int main(){
#define test123
#ifdef test
  freopen("in.txt","r",stdin);freopen("out.txt","w",stdout);
#endif
	scanf("%d",&casn);
	while(casn--){
		scanf("%d",&n);
		for(int i=1;i<=n;i++){
			scanf("%lld",num+i);
		}
		pos.clear();
		maketree();
		scanf("%d",&k);
		int a,b;
		for(int i=1;i<=k;i++){
			scanf("%d%d",&a,&b);
			ask[i]=(node2){a,b,i};
		}
		sort(ask+1,ask+1+k,cmp);
		int r=1;
		for(int i=1;i<=k;i++){
			while(r<=ask[i].r){
				if(pos[num[r]]){
					update(pos[num[r]],0);
				}
				pos[num[r]]=r;
				r++;
			}
			ans[ask[i].id]=query(ask[i].l,ask[i].r);
		}
		for(int i=1;i<=k;i++){
			printf("%lld\n",ans[i]);
		}
	}

#ifdef test
  fclose(stdin);fclose(stdout);system("out.txt");
#endif
  return 0;
}

 

转载于:https://www.cnblogs.com/nervendnig/p/9383505.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值