1. 程式人生 > 實用技巧 >旅行計劃

旅行計劃

題目描述

給定一串數 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\n",q[i].ans);
	return 0;
}