洛谷 P1972 [SDOI2009]HH的项链(离线算法+线段树/树状数组)

题目链接:[SDOI2009]HH的项链 - 洛谷

第一次做离线算法的题目,记录一下。思路参考:题解 P1972 【[SDOI2009]HH的项链】 - dlhham 的博客 - 洛谷博客

离线算法的本质是什么?是当一开始就已知所有要回答的问题的时候,把问题进行一定的排序, 然后逐个给出答案。排序的目的是使得后面的问题能用上前面得到的结论,从而减少复杂度。

考虑按照询问区间的右端点排序。对于每个问题,记录开始位置到当前右端点的所有数字的最靠右的位置,用树状数组或者线段树记录区间的贡献(对于每个点,如果是某个数字的最右位置,那么贡献为1,否则为0)


code1:树状数组 

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define For(i, a, b) for (int i = (a); i <= (b); i++)
const int N = 1e6+5;
struct Q{int l,r,id;} q[N];
int n,m,a[N],t[N],pre[N],ans[N];

bool cmp(const Q&a,const Q&b){return a.r<b.r;}
inline int lowbit(int x){return x&-x;}
inline void add(int p,int x){
	for(; p<=n; p+=lowbit(p))
		t[p]+=x;
}
inline int query(int p){
	int res=0;
	for(; p; p-=lowbit(p)) res+=t[p];
	return res;
}
signed main() {
	ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
	cin>>n; For(i,1,n) cin>>a[i];
	cin>>m; For(i,1,m) cin>>q[i].l>>q[i].r, q[i].id=i;
	sort(q+1,q+m+1,cmp);

	int cur=0;
	For(i,1,m){
		while(cur<q[i].r){
			cur++;
			if(pre[a[cur]]) add(pre[a[cur]],-1); //如果之前出现过这个数字,就删去它的影响
			add(cur,1); pre[a[cur]]=cur; //记录最后一个位置
		}
		ans[q[i].id]=query(q[i].r)-query(q[i].l-1);
	}
	For(i,1,m) cout<<ans[i]<<'\n';
}

code2:线段树(代码长一丢丢,本质方法一样)

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define For(i, a, b) for (int i = (a); i <= (b); i++)
const int N = 1e6+5;
struct Q{int l,r,id;} q[N];
int n,m,a[N],t[N<<2],pre[N],ans[N];

bool cmp(const Q&a,const Q&b){return a.r<b.r;}
void upd(int p,int l,int r,int pos,int x){
    if(l==r) {t[p]=x; return;}
    int mid = l+r>>1;
    if(pos<=mid) upd(p<<1,l,mid,pos,x);
    else upd(p<<1|1,mid+1,r,pos,x);
    t[p] = t[p<<1]+t[p<<1|1];
}
int query(int p,int l,int r,int x,int y){
    if(x<=l && r<=y) return t[p];
    int mid=l+r>>1, ans=0;
    if(x<=mid) ans+=query(p<<1,l,mid,x,y);
    if(y>mid) ans+=query(p<<1|1,mid+1,r,x,y);
    return ans;
}
signed main() {
	ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
	cin>>n; For(i,1,n) cin>>a[i];
	cin>>m; For(i,1,m) cin>>q[i].l>>q[i].r, q[i].id=i;
	sort(q+1,q+m+1,cmp);

    int cur=0;
    For(i,1,m){
        while(cur<q[i].r){
            cur++;
            if(pre[a[cur]]) upd(1,1,n,pre[a[cur]],0);
            upd(1,1,n,cur,1); pre[a[cur]]=cur;
        }
        ans[q[i].id]=query(1,1,n,q[i].l,q[i].r);
    }
    For(i,1,m) cout<<ans[i]<<'\n';
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值