第四场 hdu 6070 Dirt Ratio (线段树+二分)

http://acm.hdu.edu.cn/showproblem.php?pid=6070

题目大意:给出的序列上的数代表颜色,求子序列中不同数字的个数X与子序列长度Y中,X/Y的最小值

解题思路:思路和官方给的想法一样

值得注意的是线段树的节点中储存的是 size(l,r)+mid×l ,在建树时 mid×l 作为树节点的初始值,然后不断更新当前颜色对于 前一个相同颜色的位置+1 到 当前位置 的节点值+1,然后询问 1 到 当前位置的最小值 是否小于mid*(i+1)。

虽然最后要打印小数点后九位但是精度只要达到小数点后五位就可以了。

AC代码:4056MS

 1 #include <iostream>
 2 #include <bits/stdc++.h>
 3 using namespace std;
 4 const int maxn=60100;
 5 const double eps=1e-5;
 6 int a[maxn],flag[maxn];
 7 double val[maxn<<2],lazy[maxn<<2];
 8 int t,n;
 9 void push_up(int k)
10 {
11     val[k]=min(val[k*2],val[k*2+1]);
12 }
13 void push_down(int k)
14 {
15     if(lazy[k])
16     {
17         lazy[k*2]+=lazy[k];
18         lazy[k*2+1]+=lazy[k];
19         val[k*2]+=lazy[k];
20         val[k*2+1]+=lazy[k];
21         lazy[k]=0;
22     }
23 }
24 void build(double mid,int l,int r,int k)
25 {
26     lazy[k]=0;
27     if(l==r)
28     {
29         val[k]=l*mid;
30         return ;
31     }
32     int midd=(l+r)/2;
33     build(mid,l,midd,k*2);
34     build(mid,midd+1,r,k*2+1);
35     push_up(k);
36 }
37 void init(int L,int R,int l,int r,int k)
38 {
39     if(L<=l&&r<=R)
40     {
41         val[k]=val[k]+1;
42         lazy[k]+=1;
43         return ;
44     }
45     push_down(k);
46     int mid=(l+r)/2;
47     if(L<=mid) init(L,R,l,mid,k*2);
48     if(R>mid) init(L,R,mid+1,r,k*2+1);
49     push_up(k);
50 }
51 double query(int L,int R,int l,int r,int k)
52 {
53     if(L<=l&&r<=R)
54     {
55         return val[k];
56     }
57     push_down(k);
58     int mid=(l+r)/2;
59     double ans=1e5;
60     if(L<=mid) ans=query(L,R,l,mid,k*2);
61     if(R>mid) ans=min(ans,query(L,R,mid+1,r,k*2+1));
62     push_up(k);
63     return ans;
64 }
65 bool solve(double mid)
66 {
67     memset(flag,0,sizeof(flag));
68     build(mid,1,n,1);
69     for(int i=1;i<=n;i++)
70     {
71         init(flag[a[i]]+1,i,1,n,1);
72         flag[a[i]]=i;
73         if(query(1,i,1,n,1)<=mid*(i+1)) return true;
74     }
75     return false;
76 }
77 int main()
78 {
79
80     scanf("%d",&t);
81     //freopen("data.txt","w",stdout);
82     while(t--)
83     {
84         scanf("%d",&n);
85         for(int i=1;i<=n;i++)
86         scanf("%d",&a[i]);
87         double l=0,r=1,mid,ans;
88         while(r-l>eps)
89         {
90             mid=(l+r)/2;
91             if(solve(mid))
92             r=(ans=mid)-eps;
93             else
94             l=mid+eps;
95         }
96         printf("%.9lf\n",ans);
97     }
98     return 0;
99 }
时间: 2024-10-24 02:23:46

第四场 hdu 6070 Dirt Ratio (线段树+二分)的相关文章

hdu 6070 Dirt Ratio(分数规划)

题目链接:hdu 6070 Dirt Ratio 题意: 给你n个数,让你找一段区间[l,r],使得[l,r]中不同的数的个数size/(r-l+1)最小. 题解: Claris官方题解: 1 #include<bits/stdc++.h> 2 #define F(i,a,b) for(int i=(a);i<=(b);++i) 3 using namespace std; 4 5 const int N=6e4+7; 6 int t,n,a[N],lazy[N*4],la[N]; 7

多校第4场 HDU 4902 Nice boat 线段树

思路:这题比赛的时候宝哥说的思路我觉得对的,就是当是2操作的时候,先把数放到数组里,最后查询输出的时候再统一计算,不过那时敲得烂死了,debug了两天,靠-- 上午写的vector在pushDown的时候又忘了clear了,然后MLE了一早上,尼玛,还以为用的数组太大超了,然后又改成结构体,还是MLE,最后把别人的代码交上去发现没MLE,疯了一中午,最后无聊的时候才发现这个错误,尼玛--发现自己调试怎么变得这么弱了呢-- 还有一个需要注意的问题是1与2操作的处理上比较容易出错,这也是我WA了一下

HDU 6070 Dirt Ratio

队友已经写过一个代码了,用数组实现的线段树,自己再写了一个结构体的线段树,嗯,就是这样,线段树风格不一样而已. 题解:满足的关系:res=size(l--->r) /  (r-l+1) ,求得最小的结果.正面解题,因为不知道res,而这样做的枚举每一个区间会得到n2的复杂度,所以枚举每一个可能的答案,然后再找是否存在可以满足的这样解的区间,如果不存在,那就根据枚举的mid和siza(l--->r) /(r-l+1)的关系更改mid的值.这道题目是满足单调性的,所以可以二分答案去做. 参考代码:

2017多校第8场 HDU 6133 Army Formations 线段树合并

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6133 题意:给你一棵n个节点的二叉树,每个节点有一个提交任务的时间,每个节点总的提交任务的罚时为:提交这个节点和其子树所有的任务,每个任务提交时间的总和为该点的罚时.求每个节点提交完所有任务的最小罚时. 解法:根据题意,我们可以知道每个节点的提交的最小罚时为,按照任务的提交时间从小到大的来提交任务,可以得到最小的罚时.所以我们可以用线段树合并,先建立权值线段树,记录权值区间L到R的所有权值sum与s

HDU-DuoXiao第二场hdu 6315 Naive Operations 线段树

hdu 6315 题意:对于一个数列a,初始为0,每个a[ i ]对应一个b[i],只有在这个数字上加了b[i]次后,a[i]才会+1. 有q次操作,一种是个区间加1,一种是查询a的区间和. 思路:线段树,一开始没用lazy,TLE了,然后开始想lazy的标记,这棵线段树的特点是,每个节点维护 :一个区间某个a 要增加1所需个数的最小值,一个区间已加上的mx的最大值标记,还有就是区间和sum. (自己一开始没有想到mx标记,一度想把lazy传回去. (思路差一点就多开节点标记. #include

HDU 1823 二维线段树(区间max)

Luck and Love Time Limit: 10000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 5262    Accepted Submission(s): 1317 Problem Description 世界上上最远的距离不是相隔天涯海角而是我在你面前可你却不知道我爱你                ―― 张小娴 前段日子,枫冰叶子给Wiskey做了

HDU 4902 Nice boat(线段树)

HDU Nice boat 题目链接 题意:给定一个序列,两种操作,把一段变成x,把一段每个数字,如果他大于x,就变成他和x的gcd,求变换完后,最后的序列. 思路:线段树,每个结点多一个cover表示该位置以下区间是否数字全相同,然后每次延迟操作,最后输出的时候单点查询即可 代码: #include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int N = 1

hdu 2795 Billboard(线段树)

Billboard Time Limit: 20000/8000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 10890    Accepted Submission(s): 4827 Problem Description At the entrance to the university, there is a huge rectangular billboard of

hdu 3016 Man Down (线段树 + dp)

题目大意: 是男人就下一般层...没什么可以多说的吧. 注意只能垂直下落. 思路分析: 后面求最大值的过程很容易想到是一个dp的过程 . 因为每一个plane 都只能从左边 从右边下两种状态. 然后我们所需要处理的问题就是 ,你如何能快速知道往左边下到哪里,往右边下到哪里. 这就是线段树的预处理. 讲线段按照高度排序. 然后按照高度从小到大加入到树中. 然后去寻找左端点 和 右端点最近覆盖的线段的编号. #include <cstdio> #include <iostream> #