hdu多校第三场 1007 (hdu6609) Find the answer 线段树
题意:
给定一组数,共n个,第i次把第i个数扔进来,要求你删掉前i-1个数中的一些(不许删掉刚加进来这个数),使得前i个数相加的和小于m。问你对于每个i,最少需要删掉几个数字。
题解:
肯定是优先删大数,一开始想的方法类似于尺取,就是维护一个大顶堆作为现有的数,小顶堆作为要删的数,每次大顶堆的元素总和大于m了,就把堆顶扔进小顶堆,扔完了,再把小顶堆的堆顶扔回大顶堆,在会导致大顶堆值总和大于m时停止。
但是很容易会被1 1 1 1 1 1 1 1 1 1 100 这样的数据卡掉。
然后想到了,维护一个线段树,假想线段树根节点从左到右保存原数列中从大到小的数,但是一开始置为虚节点,值为0,而每当计算到数列的某一位时,再把这一位在线段树上该在的位置赋上实值,这个过程需要离线排序,nlogn。
二分,每次枚举节点k,在线段树上求区间和,使得1到k上,所有非虚节点和大于等于sum-m,找到最小k,然后输出1-k的非虚节点的数量,二分复杂度logn,线段树上求区间和复杂度logn。时间复杂度$O(nlog^2n)$
这个想法已经很接近正确答案了,还是T了,实际上,线段树本身就是一个二分的结构,又为什么要在线段树上二分枚举节点呢?归根结底还是我对于常用数据结构的不熟悉,只会套用板子,正解是直接在线段树上二分求前缀和,总时间复杂度$O(nlogn)$
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cassert>
#define MAXN 200005
#define LL long long
using namespace std;
struct Node{
int l,r;
LL val;
int exis;
}node[MAXN*];
void build(int l,int r,int x){
node[x].l=l;
node[x].r=r;
if(l==r){
node[x].exis=;
node[x].val=;
return ;
}else{
int mid=(l+r)/;
build(l,mid,x*);
build(mid+,r,x*+);
}
node[x].val=node[*x].val+node[*x+].val;
node[x].exis=node[*x].exis+node[*x+].exis;
return ;
}
LL query(int l,int r,int x){
if(l<=node[x].l && node[x].r<=r)return node[x].exis;
if(node[x].r<l || r<node[x].l)return ;
LL ans=;
if(l<=node[x*].r)ans+=query(l,r,*x);
if(node[x*+].l<=r)ans+=query(l,r,*x+);
return ans;
}
void add(int id,int num,int x){
if(node[x].l==node[x].r){
node[x].val=num;
node[x].exis=;
return ;
}
if(id<=node[x*].r){
add(id,num,x*);
}else{
add(id,num,x*+);
}
node[x].val=node[x*].val+node[x*+].val;
node[x].exis=node[x*].exis+node[x*+].exis;
return ;
}
int bsearch(LL last,int x){
if(node[x].l==node[x].r)return node[x].exis;
if(node[x].val==last)return node[x].exis;
if(node[x].val>last){
if(last<=node[x*].val)return bsearch(last,x*);
else return node[x*].exis+bsearch(last-node[x*].val,x*+);
}
}
struct Tag{
int index;
int val;
int rank;
}tag[MAXN];
inline bool cmpval(const Tag &a,const Tag &b){
return a.val>b.val;
}
inline bool cmpindex(const Tag &a,const Tag &b){
return a.index<b.index;
}
int main(){
int q;
scanf("%d",&q);
while(q--){
int n;
LL m;
scanf("%d %lld",&n,&m);
// memset(node,0,sizeof node);
build(,n,);
for(int i=;i<=n;i++){
scanf("%d",&tag[i].val);
tag[i].index=i;
}
sort(tag+,tag++n,cmpval);
for(int i=;i<=n;i++){
tag[i].rank=i;
}
sort(tag+,tag++n,cmpindex);
LL sum=;
for(int i=;i<=n;i++){
// printf("%d %d %d\n",tag[i].index,tag[i].val,tag[i].rank);
sum+=tag[i].val;
if(sum<=m)printf("0 ");
else printf("%d ",bsearch(sum-m,));
add(tag[i].rank,tag[i].val,);
// printf("\n");
// for(int j=1;j<=4*n;j++){
// printf("%d %d %lld %d\n",node[j].l,node[j].r,node[j].val,node[j].exis);
// }
// printf("\n");
}
printf("\n");
// for(int j=1;j<=4*n;j++){
// printf("%d %d %lld %d\n",node[j].l,node[j].r,node[j].val,node[j].exis);
// }
// printf("\n");
}
return ;
}
后记:
在群里看到了一个神仙做法。
这道题难点在于刚加进去的点,不能立即删,如果可以立即删,那么,后一个数字加进来,删哪些,一定可以通过前一个数字加进来后删哪些快速求出,贪心删去最大的就行了。
神仙的思路是,假想这个游戏允许删掉刚刚加进来的那个数字,那么,记tot[i]为第i个数字加进来后,应该删去哪些,tot[i]肯定是tot[i+1]的子集。
而实际上,第i个数字加进来后,只能删前i-1个数字,那么,临时删去一些数字,用num[i]记录临时删掉的数字,那么,此时的答案就是tot[i-1]+num[i].
贪心删去最大的就行,因此可以用multiset维护。
神仙的链接:https://blog.csdn.net/qq_41603898/article/details/97674737
hdu多校第三场 1007 (hdu6609) Find the answer 线段树的更多相关文章
- 牛客多校第三场 G Removing Stones(分治+线段树)
牛客多校第三场 G Removing Stones(分治+线段树) 题意: 给你n个数,问你有多少个长度不小于2的连续子序列,使得其中最大元素不大于所有元素和的一半 题解: 分治+线段树 线段树维护最 ...
- 2018 HDU多校第三场赛后补题
2018 HDU多校第三场赛后补题 从易到难来写吧,其中题意有些直接摘了Claris的,数据范围是就不标了. 如果需要可以去hdu题库里找.题号是6319 - 6331. L. Visual Cube ...
- [2019杭电多校第三场][hdu6609]Find the answer(线段树)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6609 大致题意是求出每个位置i最小需要将几个位置j变为0(j<i),使得$\sum_{j=1}^ ...
- hdu多校第三场
Problem D. Euler Function 思路:打表找找规律. #include<bits/stdc++.h> #define LL long long #define fi f ...
- HDU 多校 第三场 Find the answer
这题是原来cf上的一道原题,不过对于有一些数据范围修改了,不过还是很好想的 题意:给定一个长度为N的数组,对于数组中的每个位置,满足当前和小于M所需要去掉的最小代价 分析:对于当前是否需要进行去掉一些 ...
- HDU 多校 第三场 Fansblog
代码千万条,规范第一条 训练赛的时候打表找规律,发现答案是1/(st-pre-1)!,奈何用错了模板,一直TLE到比赛结束,一直以为是卡什么输入输出或者是两个素数相差太大导致复杂度过高,读入优化啥的都 ...
- 2019杭电多校第三场hdu6609 Find the answer(线段树)
Find the answer 题目传送门 解题思路 要想变0的个数最少,显然是优先把大的变成0.所以离散化,建立一颗权值线段树,维护区间和与区间元素数量,假设至少减去k才能满足条件,查询大于等于k的 ...
- hdu多校第五场1007 (hdu6630) permutation 2 dp
题意: 给你n个数,求如下限制条件下的排列数:1,第一位必须是x,2,最后一位必须是y,3,相邻两位之差小于等于2 题解: 如果x<y,那么考虑把整个数列翻转过来,减少讨论分支. 设dp[n]为 ...
- hdu多校第四场 1007 (hdu6620) Just an Old Puzzle 逆序对
题意: 给你一个数字拼图,问你数字拼图能否能复原成原来的样子. 题解: 数字拼图的性质是,逆序数奇偶相同时,可以互相转化,逆序数奇偶不同,不能互相转化. 因此统计逆序对即可. #include< ...
随机推荐
- Mycat搭建负载均衡,读写分离的Mysql集群
Mycat搭建负载均衡,读写分离的Mysql集群 准备环境 1.mysql-5.7.24-linux-glibc2.12-x86_64.tar.gz 2.Mycat-server-1.6.7.4-te ...
- Bribing FIPA
Bribing FIPA 给出多棵有n个节点的有根树,第i个节点有一个权值\(a_i\),定义一个点能控制的点为其所有的子节点和它自己,询问选出若干个点的最少的权值之和,并且能够控制大于等于m个点,\ ...
- phpstrom 注释效果
/** * .,:,,, .::,,,::. * .::::,,;;, .,;;:,,....:i: * :i,.::::,;i:. ....,,:::::::::,.... .;i:,. ..... ...
- Vue-cli中使用vConsole,以及设置JS连续点击控制vConsole按钮显隐功能实现
最近发现了一个鹅厂的仓库,实现起来比我这个方便[捂脸].https://github.com/AlloyTeam/AlloyLever 一.vue-cli脚手架中搭建的项目引入vConsole调试 1 ...
- Vue.js - 路由 vue-router 的使用详解2(参数传递)
一.使用冒号(:)的形式传递参数 1,路由列表的参数设置 (1)路由列表的 path 是可以带参数的,我们在路由配置文件(router/index.js)里以冒号的形式设置参数. (2)下面样例代码中 ...
- spring boot部署到阿里云碰到的总总问题
2375错误,我没装docker,从pom中删了吧 mysql,不能写本机对外,得写127.0.0.1. 如何生成jar包,在pom中写上jar <groupId>com.coding&l ...
- bash数组总结
bash数组操作 bash支持两种数组,一种是索引数组,一种是关联数组 索引数组 数组的值类型是任意的,索引也未必一定要连续,当做列表理解更好 下面总结下索引数组,即列表: 1. 声明 declare ...
- xslt数值的函数与xslt字符串函数
以下是xslt数值的函数与xslt字符串函数的说明与参考示例. 1.xslt数值的函数:(1)fn:number(arg) 返回参数的数值.参数可以是布尔值.字符串或节点集. 示例:<xsl:v ...
- UVA 240 Variable Radix Huffman Encoding
题目链接:https://vjudge.net/problem/UVA-240 题目大意 哈夫曼编码是一种最优编码方法.根据已知源字母表中字符出现的频率,将源字母表中字符编码为目标字母表中字符,最优的 ...
- SimpleDateFormat日期格式
前言 java中使用SimpleDateFormat类的构造函数SimpleDateFormat(String str)构造格式化日期的格式,通过format(Date date)方法将指定的日期对象 ...