暑假多校赛的一道题,印象深刻,今天终于补了,现在感觉也不是特别难,题意是很经典的那种问题,就是给你一个数列,问一个区间不同的个数比区间的长度的值,在这个数列里的最小值。之前搜过区间不同数的个数的查询问题,发现了一个叫主席数的东西。(不会)那么这道题应该是更难了一点,求不同数的个数还要比上区间的长度,求一个最小值。这个时候看到这种分数很容易想到分数规划,这道题就可以列个式子。x/ (r-l+1) <= mid; x(不同数的个数)肯定是要求的,转化成x-(r-l+1)mid <=0;即求这个式子的最小值,(mid是可以利用的定值),区间内不同数的个数这个问题,其实用神奇的线段树就可以解决了,还可以把式子转化为x + l*mid<= (r+1)*mid;不转化也可以做,线段树维护区间内 x+l*mid最小值,差不多就可以做出来了。总是是道不错的题。
时间复杂度 n log log n
#include <algorithm>
#include <bitset>
#include <cassert>
#include <climits>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <deque>
#include <iomanip>
#include <iostream>
#include <map>
#include <numeric>
#include <queue>
#include <set>
#include <stack>
#include <string>
using namespace std;
typedef long long ll;
const int maxn =60150+7;
int a[maxn];
int pre[maxn];
int lazy[maxn<<2];
double sum[maxn<<2];
int n;
void pushdown (int i){
if(lazy[i]){
int s= lazy[i];
lazy[i*2]+= s;
lazy[i*2+1]+=s;
sum[i*2]+= s;
sum[i*2+1]+= s;
lazy[i]= 0;
}
return;
}
void build(int l,int r,int i,double mid1){
lazy[i]= 0;
if(l==r){
sum[i]=mid1*l;
return;
}
int mid= (l+r)/2;
build(l,mid,i*2,mid1);
build(mid+1,r,i*2+1,mid1);
sum[i]= min(sum[i*2],sum[i*2+1]);
return;
}
void update (int l,int r,int L,int R,int v,int i){
int mid=(l+r)/2;
if (L<=l &&R>=r){
lazy[i]+=v;
sum[i]+=v;
return;
}
pushdown(i);
if (L<=mid) update (l,mid,L,R,v,i*2);
if (R>mid) update(mid+1, r, L, R, v, i*2+1);
sum[i]=min (sum[i*2],sum[i*2+1]);
return;
}
double query (int l,int r,int L,int R,int i){
if(L<=l && R>=r){
return sum[i];
}
int mid=(l+r)/2;
pushdown(i);
double ans=0x3f3f3f3f;
if(L<=mid) ans= min (ans,query(l,mid,L,R,i*2));
if (R>mid) ans =min (ans,query(mid+1,r,L,R,i*2+1) );
return ans;
}
double judge(double mid){
build(1,n,1,mid);
int i;
memset(pre,0,sizeof (pre));
for(i=1;i<=n;i++){
update(1,n,pre[a[i]]+1,i,1,1);
double x = query(1,n,1,i,1);
if (x<= (i+1)*mid) return 1;
pre[a[i]] = i;
}
return 0;
}
int main()
{
int t;
int i;
scanf ("%d",&t);
while (t--){
memset (pre,0,sizeof(pre));
scanf("%d",&n);
for (i=1;i<=n;i++){
scanf ("%d",&a[i]);
}
double l=0,r=1;
double ans;
for (i=0;i<=20;i++){
double mid=(l+r)/2;
if(judge(mid))r=mid,ans=mid;
else l= mid;
}
printf("%.10f\n",ans);
}
return 0;
}