• codeforces 505E Mr. Kitayuta vs. Bamboos 题解


    直接讲我做这题的思路吧
    我的做法好像不太一样?

    看到最小化最大值,感觉可以二分答案,答案也确实有单调性.. 然后看 check 怎么做。

    先抛开每天砍几次的限制,二分最大的那棵的高度之后,我们可以得知每棵柱子总计要被砍多少长度,进而求出总次数,先把这个判掉,如果总次数之和大于 (mk),显然不行。

    t4NaX4.png
    图中相当于把这个竹子分成四段,最优情况下,在没有第几天的限制时,我一定会让每一次砍操作都砍掉其中一段,总次数就是所有竹子的段数之和 (sum)

    注意到时间靠后的“砍”操作一定比时间靠前的更有价值,比如我对于同一个竹子,本来在第 (k) 天砍,把这次机会移到第 (k+1) 天后,这棵竹子要么长度减小要么不变,不会增加

    也就是说我可以把“砍”操作任意后移。所以我们贪心的让“砍”操作尽量在前面,然后在这样的最优方案下,保证按时间的每一个后缀和符合要求,即后 (i) 天的砍总操作数 (leq ik)

    我们先假定只能砍 (sum) 次,事实上,即使多砍,也没有任何用处,该不行的还是不行,本来行的可能弄成不行,这看起来很不对,实际上是对的。比如我本来第 (k) 天要砍一次,现在我在前面多砍了几次,然后我第 (k) 天就不砍了,这样看起来 (k) 的后缀和减少了,其实不是,第 (k) 天留下的竹子长度变长了,后面一定会有某一天多出来了一个操作,(k) 以后的操作次数增加了,这样 (k) 的后缀没变,后面某一天的后缀却变大了。

    事实上不同的竹子是相互独立的,于是我们枚举每个竹子,按时间从小到大看,只要当前长到的长度大于等于 (p),就砍 (p),否则看长度是不是大于等于顶上那段 (fir),因为把 (fir) 砍掉也能使它少掉一次需要砍的次数,不过砍了就一定砍成 0,可能会把 (fir) 下面那段 (p) 砍去一部分,要注意一下这个。
    其实上面那两种是一样的,我为啥要分类讨论呢。

    这样复杂度是 (O(nmlog W)) 的,过不了,但是在枚举天数的时候,很多次枚举都是等它长到长度足够,这没有意义,把这些合并起来一起处理,就是每次让它一下直接长到能砍(当然对应的天数也要加上去)的高度。由于最多砍 (mk) 次(否则之前判掉了),时间复杂度降为 (O((n+mk)log W))

    using namespace std;
    typedef long long LL;
    const LL N = 200005;
    
    LL n,m,k,p;
    LL to[N],h[N],dv[N],fir[N],a[N];
    LL val[5005];
    
    LL chk(LL x){
    	LL sum = 0,td;
    	for(LL i = 1;i <= n;i ++){
    		to[i] = h[i] + a[i] * m - x;
    		fir[i] = -1; dv[i] = 0;
    		if(to[i] <= 0) continue;
    		dv[i] = (to[i] - 1) / p;
    		fir[i] = to[i] - dv[i] * p;
    		sum += (dv[i] + 1);
    	} if(sum > m * k) return 0; // 判总次数
    	
    	for(LL i = 1;i <= m;i ++) val[i] = 0;
    	for(LL i = 1;i <= n;i ++){
    		to[i] = h[i];
    		for(LL j = 1;j <= m;){
    			while(to[i] >= p && dv[i]){
    				to[i] -= p; dv[i] --;
    				val[j] ++;
    			}
    			if(to[i] >= fir[i] && fir[i] != -1){
    				to[i] -= fir[i]; fir[i] = -1;
    				val[j] ++;
    			}
    			if(fir[i] == -1 && !dv[i]) break;
    			
    			td = ((fir[i] == -1 ? p : fir[i]) - to[i] - 1) / a[i];
    			to[i] += a[i] * (td + 1); j += (td + 1);
    		}
    	} sum = 0;
    	for(LL i = m;i >= 1;i --){
    		sum += val[i];
    		if(sum > k * (m - i + 1)) return 0;
    	} return 1;
    }
    
    int main(){
    	ios::sync_with_stdio(false);
    	LL mxa = 0,l,r,mid,ans;
    	cin >> n >> m >> k >> p;
    	for(LL i = 1;i <= n;i ++){
    		cin >> h[i] >> a[i];
    		mxa = max(mxa,a[i]);
            // 这是二分下界,最后一轮全部砍成 0 后,还是会再长一段,不取这个会错,见样例 2
    	}
    	
    	l = mxa,r = 0x3f3f3f3f3f3f3f3f;
    	while(l <= r){
    		mid = (l + r) >> 1;
    		if(chk(mid)){
    			ans = mid;
    			r = mid - 1;
    		}
    		else l = mid + 1;
    	}
    	cout << ans << '
    ';
    	return 0;
    }
    
  • 相关阅读:
    jquery效果,多个div,点击任何一个div,那么这个div会切换文字,变换背景颜色,再次点击其他的div ,这个div会发生刚才的变化,之前点击的div的颜色会变回来
    用js动态的改变img标签里面的src属性实现图片的循环切换
    清除浮动
    清除浮动clearfix
    转移符 个人工作中使用记录一下
    12.Django数据库操作(执行原生SQL)
    11.Django数据库操作(查)
    10.Django数据库操作(增删改)
    9.Django里的数据同步migrations命令
    8.Django模型类例子
  • 原文地址:https://www.cnblogs.com/IltzInstallBI/p/13071048.html
Copyright © 2020-2023  润新知