1. 程式人生 > >HDU 6070 線段樹

HDU 6070 線段樹

const cnblogs 分析 2.0 新的 add ans ast main

題意:求AC率,x/y 的最小值,x是區間數字的種類數,y是區間的長度。

分析:

二分答案比率。ans,

技術分享

動態插入結點,一些區間的size會發生變化,是那些前面暫時沒有新的結點的區間 size + 1。

ans*l,每一個區間只有一個ans*l,只與 l 相關,線段樹單點更新。

用線段樹維護區間的最小值。最小值小於 ans,二分左移。

#include <bits/stdc++.h>
using namespace std;

const int maxn = 60005;
const int eps = 1e-9;
int a[maxn];

int last[maxn];

double tree[maxn<<2],add[maxn<<2]; void pushdown(int root) { tree[root<<1]+=add[root]; tree[root<<1|1]+=add[root]; add[root<<1]+=add[root]; add[root<<1|1]+=add[root]; add[root]=0; } void update(int l,int r,int L,int R,int root,double
k) { if(l<=L&&R<=r) { tree[root]+=k; add[root]+=k; return ; } if(add[root]>eps)pushdown(root); int mid=L+R>>1; if(r<=mid)update(l,r,L,mid,root<<1,k); else if(l>mid)update(l,r,mid+1,R,root<<1|1,k);
else { update(l,mid,L,mid,root<<1,k); update(mid+1,r,mid+1,R,root<<1|1,k); } tree[root]=min(tree[root<<1],tree[root<<1|1]); } double query(int l,int r,int L,int R,int root) { if(l<=L&&R<=r) return tree[root]; if(add[root]>eps)pushdown(root); int mid=L+R>>1; if(r<=mid)return query(l,r,L,mid,root<<1); else if(l>mid)return query(l,r,mid+1,R,root<<1|1); else return min(query(l,mid,L,mid,root<<1),query(mid+1,r,mid+1,R,root<<1|1)); tree[root]=min(tree[root<<1],tree[root<<1|1]); } int main() { //freopen("in.txt","r",stdin); int t; scanf("%d",&t); while(t--) { int n; scanf("%d",&n); for(int i=1; i<=n; i++) scanf("%d",&a[i]); double l = 0,r=1.0; for(int g=0; g<20; g++) { double mid = (l+r)/2.0; memset(tree,0,sizeof(tree)); memset(add,0,sizeof(add)); memset(last,0,sizeof(last)); int flag = 0; for(int i=1; i<=n; i++) { update(last[a[i]]+1,i,1,n,1,1); update(i,i,1,n,1,mid*i); last[a[i]] = i; double k = query(1,i,1,n,1); if(k<=(double)mid*(i+1)) { flag=1; break; } } if(flag!=1) l = mid; else r = mid; } printf("%.10lf\n",l); } return 0; }

HDU 6070 線段樹