标签:答案 数加 记录 bool 前缀 ems while 调用 blank
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6609
题意:给你一个长为n(2e5+7)的数字序列和一个数字m(1e9),对从1开始的每段区间分析(1-1,1-2...1-n),要求区间和小于m,你可以将每段区间除右端点的数置为0,每段区间输出已经将多少数置为0了
分析:对于第i个位置,怎样选择数字才会使满足条件情况下选择数字数目最少呢?很容易想到,需要选择前i1个数中较大的数字,使其变为0
基于这个思想,如果我们对于每个位置i都暴力去找最大的前几个数,显然会TLE!
可以注意到,题目可以转化为前i-1个数中最多选出多少个数字和W[i]相加使得其和小于等于m(很容易想到,选择较小的数才会使选的数最多)。
转化之后就很容易想到用线段树来维护了。 我们对给定数组进行离散化,对于离散化之后的数组建立一颗线段树,线段树上的每个节点记录区间之和以及区间内数字个数。时间复杂度:N*log(N)
代码分析:一开始并不将线段树初始化完毕,而是一步步初始化,init()方法中nn不断乘2是为了将索引变到最下面的结点一层(如果结点一层完全的话,子节点的数目是前面所有分支数加一,因为这是一个以2为公比,1为首项的等比数列。这也是后面调用update方法时nn-1的原因),所有我们一般初始化线段树是扩大四倍。
代码中用到了离散化,记得好好看一看。
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int inf=0x3f3f3f3f;//这个数是1e9数量级的,且可以用memset函数 const int maxn=2e5+7; const double pi=acos(-1); const int mod=1e9+7; ll sum[4*maxn],sums[4*maxn];//分别是区间和,以及区间内数字的个数 ll qzh[maxn];//前缀和 struct node{ ll v,id; bool operator <(const node &x) const{ if(v==x.v) return id<x.id; return v>x.v; } }a[maxn],b[maxn]; int ans=0,nn;//ans用来记录答案 void init(int n){ nn=1; while(nn<n) nn*=2; } void query(int k,int l,int r,ll u){ int mid=(l+r)/2; if(u==0) return ; if(r-l==1){ ans+=sums[k]; return ; } if(sum[2*k]<=u) u-=sum[2*k],ans+=sums[2*k],query(k*2+1,mid,r,u);//如果左边的不够减,就搜右边的 else{ query(k*2,l,mid,u);//否则,就继续往左边搜 } } void update(int k,int v){ if(k==0)return; sum[k]+=v; sums[k]++; update(k/2,v); } int main(){ int Q;scanf("%d",&Q); while(Q--){ memset(sum,0,sizeof(sum)); memset(sums,0,sizeof(sums)); int n;ll m;scanf("%d %lld",&n,&m); init(n); for(int i=1;i<=n;i++){ scanf("%lld",&a[i].v); b[i].id=i; b[i].v=a[i].v; qzh[i]=qzh[i-1]+a[i].v; } sort(b+1,b+1+n); for(int i=1;i<=n;i++){ a[b[i].id].id=i; }//离散化,目的就是使a数组的id标的是其在a数组中的大小排名 for(int i=1;i<=n;i++){ ll t=qzh[i]-m; ans=0; if(t>0) { query(1,1,nn,t); } printf("%d ",ans); update(a[i].id+nn-1,a[i].v); } cout<<endl; } return 0; }
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int inf=0x3f3f3f3f;//这个数是1e9数量级的,且可以用memset函数 const int maxn=2e5+7; const double pi=acos(-1); const int mod=1e9+7; ll sum[4*maxn],sums[4*maxn];//分别是区间和,以及区间内数字的个数 ll qzh[maxn];//前缀和 struct node{ ll v,id; bool operator <(const node &x) const{ if(v==x.v) return id<x.id; return v>x.v; } }a[maxn],b[maxn]; int ans=0,nn;//ans用来记录答案 void init(int n){ nn=1; while(nn<n) nn*=2; } void query(int k,int l,int r,ll u){ int mid=(l+r)/2; if(u==0) return ; if(r-l==1){ ans+=sums[k]; return ; } if(sum[2*k]<=u) u-=sum[2*k],ans+=sums[2*k],query(k*2+1,mid,r,u);//如果左边的不够减,就搜右边的 else{ query(k*2,l,mid,u);//否则,就继续往左边搜 } } void update(int k,int v){ if(k==0)return; sum[k]+=v; sums[k]++; update(k/2,v); } int main(){ int Q;scanf("%d",&Q); while(Q--){ memset(sum,0,sizeof(sum)); memset(sums,0,sizeof(sums)); int n;ll m;scanf("%d %lld",&n,&m); init(n); for(int i=1;i<=n;i++){ scanf("%lld",&a[i].v); b[i].id=i; b[i].v=a[i].v; qzh[i]=qzh[i-1]+a[i].v; } sort(b+1,b+1+n); for(int i=1;i<=n;i++){ a[b[i].id].id=i; }//离散化,目的就是使a数组的id标的是其在a数组中的大小排名 for(int i=1;i<=n;i++){ ll t=qzh[i]-m; ans=0; if(t>0) { query(1,1,nn,t); } printf("%d ",ans); update(a[i].id+nn-1,a[i].v); } cout<<endl; } return 0; }
标签:答案 数加 记录 bool 前缀 ems while 调用 blank
原文地址:https://www.cnblogs.com/qingjiuling/p/11269260.html