Codeforces 1469 F. Power Sockets —— 二分+线段树,贪心

2024-04-06 23:18

本文主要是介绍Codeforces 1469 F. Power Sockets —— 二分+线段树,贪心,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

This way

题意:

现在有一个根节点,和n条包含a[i]个节点的链。一开始所有点的颜色是白色的。你每次可以做以下操作:
找到树中某个白色节点,拿出一条链,将这个节点和链上某个节点连接,并且这两个点的颜色变成黑色,之后这条链属于树中一个部分。
你可以合并任意的链,问你离根节点第k远的白色点的深度最小是多少。

题解:

首先知道了一点:加入一条链的时候,两个白点会变成黑色,那么长度小于等于2的必不用加入。
并且长的链在前面一定是更优的,因为短链在前,再加长链的话,会使得更多的白点深度增加。
链接树上的白点时,一定是找深度最小的,连接链的时候,一定是链最中间的点。就像刚才说的一样,如果不是深度最小的话,更多的白点深度会增加。连接中间点的话,链上会有一半的点的深度降低。
但是我们不能白点数量一到k的时候就结束操作,因为可能能够在一些深度较低的白点上加链使得第k远的白点的深度减小。
于是这种最大值最小的题目我们一眼就知道是二分。
那么怎么知道当前第k远的点的深度,怎么知道深度最低的点的位置呢,使用线段树维护即可。

#include<bits/stdc++.h>
using namespace std;
const int N=8e5+5;
#define ll long long 
ll sum[N*4],f[N*4];
void push_down(int l,int r,int root){if(!f[root])return ;int mid=l+r>>1;sum[root<<1]+=(mid-l+1)*f[root];sum[root<<1|1]+=(r-mid)*f[root];f[root<<1]+=f[root];f[root<<1|1]+=f[root];f[root]=0;
}
void update(int l,int r,int root,int ql,int qr,int v){if(l>=ql&&r<=qr){sum[root]+=(r-l+1)*v;f[root]+=v;return ;}int mid=l+r>>1;push_down(l,r,root);if(mid>=ql)update(l,mid,root<<1,ql,qr,v);if(mid<qr)update(mid+1,r,root<<1|1,ql,qr,v);sum[root]=sum[root<<1]+sum[root<<1|1];
}
int q_k(int l,int r,int root,int k){if(l==r)return l;int mid=l+r>>1;push_down(l,r,root);if(sum[root<<1]>=k)return q_k(l,mid,root<<1,k);else return q_k(mid+1,r,root<<1|1,k-sum[root<<1]);
}
int q_f(int l,int r,int root){if(l==r)return l;int mid=l+r>>1;push_down(l,r,root);if(sum[root<<1])return q_f(l,mid,root<<1);else return q_f(mid+1,r,root<<1|1);
}
int a[N],n,k;
bool cmp(int x,int y){return x>y;}
int check(int x){memset(sum,0,sizeof(sum)),memset(f,0,sizeof(f));update(1,N-1,1,2,1+(a[1]-1)/2,2);if(a[1]%2==0)update(1,N-1,1,1+a[1]/2,1+a[1]/2,1);for(int i=2;i<=n;i++){if(sum[1]>=k)if(q_k(1,N-1,1,k)<=x)return 1;if(a[i]<=2)return 0;int p=q_f(1,N-1,1);update(1,N-1,1,p,p,-1);update(1,N-1,1,p+2,min(N-1,p+1+(a[i]-1)/2),2);if(a[i]%2==0&&p+1+a[i]/2<=N-1)update(1,N-1,1,p+1+a[i]/2,p+1+a[i]/2,1);}if(sum[1]>=k)if(q_k(1,N-1,1,k)<=x)return 1;return 0;
}
int main()
{scanf("%d%d",&n,&k);for(int i=1;i<=n;i++)scanf("%d",&a[i]);sort(a+1,a+1+n,cmp);if(a[1]==2)return 0*printf("-1\n");int l=1,r=N-1,mid,ans=-1;while(r>=l){mid=l+r>>1;if(check(mid))r=mid-1,ans=mid;else l=mid+1;}printf("%d\n",ans?ans:-1);return 0;
}

这篇关于Codeforces 1469 F. Power Sockets —— 二分+线段树,贪心的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/881084

相关文章

hdu2241(二分+合并数组)

题意:判断是否存在a+b+c = x,a,b,c分别属于集合A,B,C 如果用暴力会超时,所以这里用到了数组合并,将b,c数组合并成d,d数组存的是b,c数组元素的和,然后对d数组进行二分就可以了 代码如下(附注释): #include<iostream>#include<algorithm>#include<cstring>#include<stack>#include<que

hdu2289(简单二分)

虽说是简单二分,但是我还是wa死了  题意:已知圆台的体积,求高度 首先要知道圆台体积怎么求:设上下底的半径分别为r1,r2,高为h,V = PI*(r1*r1+r1*r2+r2*r2)*h/3 然后以h进行二分 代码如下: #include<iostream>#include<algorithm>#include<cstring>#include<stack>#includ

poj3468(线段树成段更新模板题)

题意:包括两个操作:1、将[a.b]上的数字加上v;2、查询区间[a,b]上的和 下面的介绍是下解题思路: 首先介绍  lazy-tag思想:用一个变量记录每一个线段树节点的变化值,当这部分线段的一致性被破坏我们就将这个变化值传递给子区间,大大增加了线段树的效率。 比如现在需要对[a,b]区间值进行加c操作,那么就从根节点[1,n]开始调用update函数进行操作,如果刚好执行到一个子节点,

hdu1394(线段树点更新的应用)

题意:求一个序列经过一定的操作得到的序列的最小逆序数 这题会用到逆序数的一个性质,在0到n-1这些数字组成的乱序排列,将第一个数字A移到最后一位,得到的逆序数为res-a+(n-a-1) 知道上面的知识点后,可以用暴力来解 代码如下: #include<iostream>#include<algorithm>#include<cstring>#include<stack>#in

hdu1689(线段树成段更新)

两种操作:1、set区间[a,b]上数字为v;2、查询[ 1 , n ]上的sum 代码如下: #include<iostream>#include<algorithm>#include<cstring>#include<stack>#include<queue>#include<set>#include<map>#include<stdio.h>#include<stdl

usaco 1.3 Barn Repair(贪心)

思路:用上M块木板时有 M-1 个间隙。目标是让总间隙最大。将相邻两个有牛的牛棚之间间隔的牛棚数排序,选取最大的M-1个作为间隙,其余地方用木板盖住。 做法: 1.若,板(M) 的数目大于或等于 牛棚中有牛的数目(C),则 目测 给每个牛牛发一个板就为最小的需求~ 2.否则,先对 牛牛们的门牌号排序,然后 用一个数组 blank[ ] 记录两门牌号之间的距离,然后 用数组 an

hdu 1754 I Hate It(线段树,单点更新,区间最值)

题意是求一个线段中的最大数。 线段树的模板题,试用了一下交大的模板。效率有点略低。 代码: #include <stdio.h>#include <string.h>#define TREE_SIZE (1 << (20))//const int TREE_SIZE = 200000 + 10;int max(int a, int b){return a > b ? a :

hdu 1166 敌兵布阵(树状数组 or 线段树)

题意是求一个线段的和,在线段上可以进行加减的修改。 树状数组的模板题。 代码: #include <stdio.h>#include <string.h>const int maxn = 50000 + 1;int c[maxn];int n;int lowbit(int x){return x & -x;}void add(int x, int num){while

poj 3190 优先队列+贪心

题意: 有n头牛,分别给他们挤奶的时间。 然后每头牛挤奶的时候都要在一个stall里面,并且每个stall每次只能占用一头牛。 问最少需要多少个stall,并输出每头牛所在的stall。 e.g 样例: INPUT: 51 102 43 65 84 7 OUTPUT: 412324 HINT: Explanation of the s

poj 2976 分数规划二分贪心(部分对总体的贡献度) poj 3111

poj 2976: 题意: 在n场考试中,每场考试共有b题,答对的题目有a题。 允许去掉k场考试,求能达到的最高正确率是多少。 解析: 假设已知准确率为x,则每场考试对于准确率的贡献值为: a - b * x,将贡献值大的排序排在前面舍弃掉后k个。 然后二分x就行了。 代码: #include <iostream>#include <cstdio>#incl