题目链接
http://acm.hdu.edu.cn/showproblem.php?pid=2795
解题思路
虽然做的是线段树专题,甚至直到要维护区间最大值,但是依然不知道怎么实现。
虽然题目很简单,但思想不好想。
画个图吧,不好说。
也就是维护的是l ~ r行中的所有行中剩余空间的最大值。
AC代码
放了俩代码
代码1是用结构体实现的tree数组
代码2是用参数实现tree左右端点的
习惯哪个看哪个吧
//代码1
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10;
int h,w,n,wi;
struct TREE
{
int l,r,res;
}tr[N<<2];
void Build(int i,int l,int r)
{
tr[i].l=l;
tr[i].r=r;
tr[i].res=w;//初始剩余空间的最大值为w
if(l==r) return ;
int mid=l+r>>1;
Build(i<<1,l,mid);
Build(i<<1|1,mid+1,r);
return ;
}
int Query(int i,int wii)
{
int res;
if(tr[i].l==tr[i].r)
{
tr[i].res-=wii;
return tr[i].l;
}
if(tr[i<<1].res>=wii) res=Query(i<<1,wii);//if语句中是左子树的剩余最大空间是否大于等于wii
else res=Query(i<<1|1,wii);
tr[i].res=max(tr[i<<1].res,tr[i<<1|1].res);//!!!
return res;
}
int main()
{
while(~scanf("%d%d%d",&h,&w,&n))
{
if(h>n) h=n;
Build(1,1,h);//!!注意右边是h,不是h<<2(应该只是我傻)
for(int i=1;i<=n;i++)
{
scanf("%d",&wi);
if(tr[1].res<wi) puts("-1");
else printf("%d\n",Query(1,wi));
}
}
return 0;
}
//代码2
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+100;
int tr[N<<2],wi,h,w,n;
int Query(int i,int l,int r,int wii)
{
if(l==r)
{
tr[i]-=wii;
return l;
}
int mid=l+r>>1,res;
if(tr[i<<1]>=wii) res=Query(i<<1,l,mid,wii);
else res=Query(i<<1|1,mid+1,r,wii);
tr[i]=max(tr[i<<1],tr[i<<1|1]);//!!!
return res;
}
int main()
{
while(~scanf("%d%d%d",&h,&w,&n))
{
if(h>n) h=n;
for(int i=1;i<=(h<<2);i++) tr[i]=w;
for(int i=1;i<=n;i++)
{
scanf("%d",&wi);
if(wi>tr[1]) puts("-1");
else printf("%d\n",Query(1,1,h,wi));
}
}
return 0;
}
总结
做的前几个线段树的题目都是纯板子题,这次稍微见识到如何运用线段树降低时间复杂度了。
说到底,线段树只是一种工具,而不是算法。
就本题而言,纯数组模拟当然可以,甚至用线段树实现的过程也是在模拟,但是由于线段树的特性,使得时间复杂度降低,优化了算法。

京公网安备 11010502036488号