题意:一块宽w高h的板,有n张纸条,线条高度为1,第i张纸条的宽度为wi,将这n张纸条按顺序从上到下,从左到右贴在那块板上,输出各纸条贴在了第几行,无法贴的输出-1。 (1 <= h,w <= 10^9; 1 <= n <= 200,000)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2795
——>>线段树过,将块的高度作为线段树区间,维护各结点对应的最大容纳量就好。开始建树时我直接来了个build(1, 1, h),结果是RE,我想数组没开小,又不用指针,怎么会是RE?于是换了建树方法,直接把4*maxn个结点全部赋为w,结果还是RE,我想会不会是太大了,于是来个二分测试,submit了几十次,测出了给前400021个结点赋w时,返回的都是WA,给400022以上个结点赋w时,返回的都是RE,my god!这怎么过?再看一下别人的代码,发现有个if(h > n) h = n;东东,顿悟了!h <= 10^9,如果测试数据中来个h = 10^9,查询线段树query(1, 1, h)是一定RE的!!!如果板的高度h大于纸条的张数n,即使n张纸条全部贴下,最多就占n行,即使此时h = 10^9,如果板的高度h小于等于纸条的张数n,线段树区间的右端只能是h。所以不能直接用h或者n来做线段树的右端,应选h,但当if(h > n) h = n。
#include <cstdio> #include <algorithm> using namespace std; const int maxn = 200000 + 10; int maxv[maxn<<2], w, wi; int query(int o, int L, int R) //查询&更新 { if(maxv[o] < wi) return -1; //不满足 if(L == R && maxv[o] >= wi) //叶子结点满足时,更新返回 { maxv[o] -= wi; return L; } int M = L + (R - L) / 2, ans; if(maxv[2*o] >= wi) ans = query(2*o, L, M); //先左后右 else ans = query(2*o+1, M+1, R); maxv[o] = max(maxv[2*o], maxv[2*o+1]); //更新 return ans; } void build(int o, int L, int R) //建树 { if(L == R) { maxv[o] = w; return; } int M = L + (R - L) / 2; build(2*o, L, M); build(2*o+1, M+1, R); maxv[o] = w; } int main() { int h, n, i; while(~scanf("%d%d%d", &h, &w, &n)) { if(h > n) h = n; //这句千万别少!!!直接用h来建树RE,直接用n建树WA build(1, 1, h); for(i = 1; i <= n; i++) { scanf("%d", &wi); printf("%d\n", query(1, 1, h)); } } return 0; }