题意:我们现在用一种容量为c的容器分两种方法去装下n个物品,n个物品的体积给定,必须按照顺序来存放物品,求两种方法分别用多少容器。
第一种方法:我们每次找能放下目前的容器,都放不下就新开一个容器
第二种方法:我们每次找第一个剩余容量大于等于当前要放入物品体积的容器,没有就新开一个
第一种方法采用线段树,单点修改,区间查询,维护区间最值
第二种方法使用map来做,map用来记录每种剩余容量的容器有多少。记得当这种剩余容量的容器的数量为0时将此容器删除
#include <bits/stdc++.h>
using namespace std;
#define N 1000000+100
#define ls p<<1
#define rs p<<1|1
#define mid ((l+r)>>1)
int a[N];
int n,c;
int tree[N<<2];
void up(int p)
{
tree[p]=tree[ls]>tree[rs]?tree[ls]:tree[rs];
}
void build(int l,int r,int p)
{
if(l==r)
{
tree[p]=c;
return;
}
build(l,mid,ls);
build(mid+1,r,rs);
up(p);
}
void update(int l,int r,int pos,int p,int k)
{
if(l==r)
{
tree[p]-=k;
return;
}
if(pos<=mid)
update(l,mid,pos,ls,k);
if(pos>mid)
update(mid+1,r,pos,rs,k);
up(p);
}
int query(int l,int r,int p,int k)
{
if(l==r)
{
return l;
}
int ans=0;
if(tree[ls]>=k)
{
ans=query(l,mid,ls,k);
}
else if(tree[rs]>=k)
{
ans=query(mid+1,r,rs,k);
}
return ans;
}
int main()
{
int t;
for(cin>>t;t;t--)
{
cin>>n>>c;
for(register int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
}
build(1,n,1);
int size1=0;
for(int i=1;i<=n;i++)
{
int bag=query(1,n,1,a[i]);
if(bag>size1)
{
size1++;
}
update(1,n,bag,1,a[i]);
}
return 0;
}
第二种方法
#include <bits/stdc++.h>
using namespace std;
#define N 1000000+100
int a[N];
int n,c;
int main()
{
int t;
for(cin>>t;t;t--)
{
cin>>n>>c;
for(register int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
}
map<int,int> mp;
mp[c-a[1]]++;
for(int i=2;i<=n;i++)
{
map<int,int>::iterator p=mp.lower_bound(a[i]);
if(p==mp.end())
{
mp[c-a[i]]++;
}
else
{
mp[p->first]--;
mp[(p->first)-a[i]]++;
if(mp[p->first]==0)
mp.erase(p);
}
}
int size2=0;
for(auto x:mp)
{
size2+=x.second;
}
printf("%d %d\n",size1,size2);
}
return 0;
}
AC代码
#include <bits/stdc++.h>
using namespace std;
#define N 1000000+100
#define ls p<<1
#define rs p<<1|1
#define mid ((l+r)>>1)
int a[N];
int n,c;
int tree[N<<2];
void up(int p)
{
tree[p]=tree[ls]>tree[rs]?tree[ls]:tree[rs];
}
void build(int l,int r,int p)
{
if(l==r)
{
tree[p]=c;
return;
}
build(l,mid,ls);
build(mid+1,r,rs);
up(p);
}
void update(int l,int r,int pos,int p,int k)
{
if(l==r)
{
tree[p]-=k;
return;
}
if(pos<=mid)
update(l,mid,pos,ls,k);
if(pos>mid)
update(mid+1,r,pos,rs,k);
up(p);
}
int query(int l,int r,int p,int k)
{
if(l==r)
{
return l;
}
int ans=0;
if(tree[ls]>=k)
{
ans=query(l,mid,ls,k);
}
else if(tree[rs]>=k)
{
ans=query(mid+1,r,rs,k);
}
return ans;
}
int main()
{
int t;
for(cin>>t;t;t--)
{
cin>>n>>c;
for(register int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
}
build(1,n,1);
int size1=0;
for(int i=1;i<=n;i++)
{
int bag=query(1,n,1,a[i]);
if(bag>size1)
{
size1++;
}
update(1,n,bag,1,a[i]);
}
map<int,int> mp;
mp[c-a[1]]++;
for(int i=2;i<=n;i++)
{
map<int,int>::iterator p=mp.lower_bound(a[i]);
if(p==mp.end())
{
mp[c-a[i]]++;
}
else
{
mp[p->first]--;
mp[(p->first)-a[i]]++;
if(mp[p->first]==0)
mp.erase(p);
}
}
int size2=0;
for(auto x:mp)
{
size2+=x.second;
}
printf("%d %d\n",size1,size2);
}
return 0;
}
总结
这题可以说出的非常好了,和当年的E题的主席树作为标准的银牌题。
通过这题我复习了线段树,并且对线段树有了新的理解,比如lazytag是干什么的什么时候不用他和lazytag之间互相影响时优先级的设置,pushdown操作的具体实现,pushup操作就是干什么的,也丰富了我关于map的知识。