旅行计划

题目描述

给定一串数 a下标从1到n,并给出 q 组询问,每次询问[k,l]中满足 k<=i<j<=l 且[i,j]中出现不同的数超过m种的(j-i)之和

Solution

首先求出对于每个j,能使他中间出现m个数的位置,记作e[j],那么对于每个e[j]>=k,区间[e[j]-1,j]...[k,j]都是合法的

因为 ej>=k 的条件已经包含了j>=k,所以只需查询 j≤l 且 e j ≥k 的所有 e[j] 的和、j的和、S(e[j])的和、j的个数。这里有两个限制,可以通过排序解决一个限制,另一个用树状数组维护。可以将询问离线后按右端点 l 排序,然后从小到大枚举右端点 j,先将当前 j的四个值加入到树状数组的 e
j 位置处(此时树状数组中存的是所有j'<=j 的值),再处理所有 l=j 的询问,即查询此时树状数组中所有 ≥k 处的和。


/*Code by Codercjh*/
#include<bits/stdc++.h>
#define fr(i,a,b) for(int i=(a);i<=(b);++i)
#define rf(i,a,b) for(int i=(a);i>=(b);--i)
#define min(a,b) (a<b?a:b)
#define max(a,b) (a>b?a:b)
using namespace std;
typedef long long ll;
template<typename T>
inline void read(T &x){
	char c=getchar();T fh=0;bool f=false;
	while(!isdigit(c))f|=(c=='-'),c=getchar();
	while(isdigit(c))fh=(fh<<1)+(fh<<3)+(c^48),c=getchar();
	x=f?-fh:fh;
	return;
}
const int N=1e5+5;
int a[N],n,m,t,cnt[N],e[N];
struct node{int l,r,id;ll ans;}q[N];
bool cmp(node a,node b){return a.r<b.r;}
bool cmp1(node a,node b){return a.id<b.id;}
ll tr[N][5],s[N];
#define lowbit(x) (x&-x)
inline void add(int x,ll v,int tp){for(;x<=n;x+=lowbit(x))tr[x][tp]+=v;}
inline ll ask(int x,int tp){ll ans=0;for(;x;x-=lowbit(x))ans+=tr[x][tp];return ans;}
void Add(int x){
	if(!e[x])return;
	add(e[x],1ll*x*e[x],1);
	add(e[x],x,2);
	add(e[x],s[e[x]],3);
	add(e[x],1,4);
}
ll Ask(int l,int r){
	ll t1=ask(r,1)-ask(l-1,1),
		t2=ask(r,2)-ask(l-1,2),
		t3=ask(r,3)-ask(l-1,3),
		t4=ask(r,4)-ask(l-1,4);
	return t1-t2*(l-1)-t3+s[l-1]*t4;
}
int main(){
	read(n),read(m),read(t);
	fr(i,1,n)read(a[i]),s[i]=s[i-1]+i;
	fr(i,1,t)read(q[i].l),read(q[i].r),q[i].id=i;
	sort(q+1,q+t+1,cmp);
	int i=1,j=1,tot=1;cnt[a[1]]=1;
	while(j<=n){
		while(tot>=m&&i<=n){
			if(tot==m&&cnt[a[i]]==1)break;
			if(--cnt[a[i]]==0)--tot;
			++i;
		}
		if(tot>=m)e[j]=i;
		if(++cnt[a[++j]]==1)++tot;
	}
	j=0;
	fr(i,1,t){
		while(++j<=q[i].r)Add(j);if(j>q[i].r)--j;
		q[i].ans=Ask(q[i].l,q[i].r);
	}
	sort(q+1,q+t+1,cmp1);
	fr(i,1,n)printf("%lld
",q[i].ans);
	return 0;
}
原文地址:https://www.cnblogs.com/coder-cjh/p/13525690.html