好久没有写的博客了,让我们从二分答案这个使用且神奇的算法开始重写博客吧。
P6733 「Wdsr-2」间歇泉 \color{green}{\texttt{P6733 「Wdsr-2」间歇泉}} P6733 「Wdsr-2」间歇泉
[Problem] \color{blue}{\texttt{[Problem]}} [Problem]
- n n n 杯水,每杯水的体积为 v i v_i vi,温度为 t i t_i ti,把它们两两合并,得到新的 n × ( n − 1 ) 2 \dfrac{n \times (n-1)}{2} 2n×(n−1) 杯水。求新的水中第 k k k 高的温度。
- 两杯水 i , j i,j i,j 合并后得到的水温为 t i × v i + t j × v j v i + v j \dfrac{t_i \times v_i + t_j \times v_j}{v_i+v_j} vi+vjti×vi+tj×vj。
[Solution] \color{blue}{\texttt{[Solution]}} [Solution]
先二分答案
mid
\texttt{mid}
mid 表示第
k
k
k 高的水温
≥
mid
\geq \texttt{mid}
≥mid,现在考虑 check()
函数。
改写一下那个式子:
t i × v i + t j × v j v i + v j ≥ mid t i × v i + t j × v j ≥ mid × v i + mid × v j t i × v i − mid × v i ≥ mid × v j − t j × v j \begin{aligned} \dfrac{t_i \times v_i + t_j \times v_j}{v_i+v_j}& \geq \texttt{mid} \\ t_i \times v_i+t_j \times v_j & \geq \texttt{mid} \times v_i+\texttt{mid} \times v_j \\ t_i \times v_i-\texttt{mid} \times v_i&\geq \texttt{mid} \times v_j-t_j \times v_j \end{aligned} vi+vjti×vi+tj×vjti×vi+tj×vjti×vi−mid×vi≥mid≥mid×vi+mid×vj≥mid×vj−tj×vj
不等式两边的值是确定的,所以我们可以提前算出,存入两个数组内。
接着,我们将它们排序,然后就可以用尺取法 O ( n ) O(n) O(n) 的做了。
时间复杂度: O ( n × log n × log ( max 1 ≤ i ≤ n v i ) ) O\left(n \times \log n \times \log \left (\max\limits_{1 \leq i \leq n}v_i \right ) \right ) O(n×logn×log(1≤i≤nmaxvi))。
[code] \color{blue}{\texttt{[code]}} [code]
const int N=1e5+100;
const double eps=1e-3;
double l,r,mid,p[N],q[N];
int a[N],c[N],n;ll k;//含义如题
inline bool check(double mid){
register long long tot=0ll;
for(int i=1;i<=n;i++){
double x=1.0*a[i]*c[i],y=mid*a[i];
p[i]=x-y;q[i]=y-x;//等式的左边和右边
if (q[i]-p[i]<eps) tot--;//自己不混自己
}
sort(p+1,p+n+1);//排序
sort(q+1,q+n+1);//排序
for(int i=1,j=1;i<=n;i++){
while (j<=n&&q[j]-p[i]<eps) j++;
tot+=j-1;//注意应该要累加j-1
}
return tot/2<k;
}
const double inf=1e15;
int main(){
n=read();k=read();
for(int i=1;i<=n;i++){
a[i]=read();c[i]=read();
}
l=1;r=inf;//init
while (l+1e-6<r){
mid=(l+r)/2.0;
if (check(mid)) r=mid;
else l=mid;
}
printf("%.5lf",l);
return 0;
}
[Algorithm] \color{blue}{\texttt{[Algorithm]}} [Algorithm] 二分+数学+排序+尺取法。
喂养宠物 \color{green}{\texttt{喂养宠物}} 喂养宠物
[Problem] \color{blue}{\texttt{[Problem]}} [Problem]
- n n n 个宠物可以选择,每个宠物有两个量: a i , b i a_i,b_i ai,bi,表示喂养 k k k 只宠物时,这只宠物需要 ( a i × ( k − 1 ) + b i ) (a_i \times (k-1) +b_i) (ai×(k−1)+bi) 的食物。保证 a i , b i > 0 a_i,b_i>0 ai,bi>0。
- 你现在只有 m m m 的食物,问最多可以养多少只宠物。
[Solution] \color{blue}{\texttt{[Solution]}} [Solution]
因为每只宠物的食量随养的宠物的量增大而增大,所以我们可以二分。
二分养多少只宠物,算出每只宠物需要的食量,贪心选即可。
[code] \color{blue}{\texttt{[code]}} [code]
int eat[55],add[55],e[55];
int total_food,n,l,r,mid,ans;
inline bool check(int mid){
for(int i=1;i<=n;i++)
e[i]=eat[i]+(mid-1)*add[i];
sort(e+1,e+n+1);//贪心选择
for(int i=1,tot=0;i<=mid;i++)
if ((tot+=e[i])>total_food)
return false;//不符题意
return true;//符合题意
}
int main(){
scanf("%d%d",&n,&total_food);
for(int i=1;i<=n;i++)
scanf("%d",&eat[i]);
for(int i=1;i<=n;i++)
scanf("%d",&add[i]);
l=1;r=n;ans=-1;
while (l<=r){
mid=(l+r)>>1;
if (check(mid)){
ans=mid;
l=mid+1;
}
else r=mid-1;
}
printf("%d",ans);
return 0;
}