zoukankan      html  css  js  c++  java
  • 【题解】 闷声刷大题 带悔贪心+wqs二分

    Legend

    大神犇 YCY 现在有 (k) 道神题要刷(假定神题没有区别),刷一道题要分两个步骤,必须要先想出正解再写出正解。

    他一共有 (n) 天时间可以刷题,但他每天最多只能想出一道题和写出一道题,可以在同一天想出和写出同一道题。

    定义刷一道题的代价为想的代价 (a_i) + 写的代价$ b_i$,YCY 想最小化 (k) 道题的代价和。

    (n le 150\,000),代价都是正数。

    Editorial

    首先这个题有个很显然的费用流搞法,显然就 TLE 了。

    其次有个 (O(n^3)) 的 dp,设 (dp_{i,j,k}) 表示考虑第 (i) 想题和第 (j) 天做题匹配,已经做完了 (k) 道题的最小代价。

    转移则按是否 (i,j) 配对来转移,不配对则 (i gets i+1)(j gets j+1),否则两个同时 (+1)

    这个 dp 可以被优化到 (O(n^2log (a+b))),第三维可以通过 (wqs) 二分搞掉。

    为什么是凸的?

    你可以看成一个二分图的带权匹配,这显然是个下凸函数。考虑它如果不下凸,则我们可以通过构造更优解来进行反证。

    现在复杂度的瓶颈在于 (O(n^2)) 的 dp,它实在是太慢了,有没有什么其它搞法?

    答案是有的,就是带悔贪心。

    考虑维护一个待选题目集合 (S),我们从左到右加入“想题目”,同时每天考虑是否“做题目”。

    如果做题目 + 想题目的代价 (le 0),我们就考虑形成这个配对,否则不形成。

    但是之后出现了更优的“做题目”,怎么办?我们要把当前的“想”配对的“做”撤回,即往 (S) 中加入负的“做”的代价。

    复杂度 (O(nlog n log (a+b)))

    Code

    特别地,根据二分的实现方法,你需要为“想”和撤回“做”两种不同的贪心方式定义先后顺序,

    因为先后顺序的不同会导致 wqs 二分到的位置不一。

    #include <bits/stdc++.h>
    
    #define debug(...) fprintf(stderr ,__VA_ARGS__)
    #define __FILE(x)
    	freopen(#x".in" ,"r" ,stdin);
    	freopen(#x".out" ,"w" ,stdout)
    #define LL long long
    
    const int MX = 150000 + 23;
    
    using namespace std;
    
    int read(){
    	char k = getchar(); int x = 0;
    	while(k < '0' || k > '9') k = getchar();
    	while(k >= '0' && k <= '9') x = x * 10 + k - '0' ,k = getchar();
    	return x;
    }
    
    int n ,k;
    int a[MX] ,b[MX];
    LL ans;
    
    struct GOOD{
    	int type;
    	LL x;
    	bool operator <(const GOOD& B)const{
    		return x == B.x ? type > B.type : x > B.x;
    	}
    };
    
    int check(LL mid){
    	int choose = 0;
    	ans = 0;
    	priority_queue<GOOD> q;
    	for(int i = 1 ; i <= n ; ++i){
    		q.push((GOOD){1 ,a[i] - mid});
    		if(!q.empty() && q.top().x + b[i] <= 0){
    			if(q.top().type == 1) ++choose;
    			ans += b[i] + q.top().x;
    			q.pop();
    			q.push((GOOD){2 ,-b[i]});
    		}
    	}
    	return choose;
    }
    
    int main(){
    	n = read() ,k = read();
    	for(int i = 1 ; i <= n ; ++i) a[i] = read();
    	for(int i = 1 ; i <= n ; ++i) b[i] = read();
    	LL l = 0 ,r = 2e9 ,mid;
    	while(l <= r){
    		// debug("%d %d
    " ,l ,r);
    		int mid = (l + r) / 2;
    		if(check(mid) >= k) r = mid - 1;
    		else l = mid + 1;	
    	}
    	check(r + 1);
    	printf("%lld
    " ,ans + (r + 1LL) * k);
    	return 0;
    }
    
  • 相关阅读:
    冲刺第二阶段(三)
    冲刺第二阶段(二)
    第二阶段——个人工作总结DAY09
    第二阶段——个人工作总结DAY08
    第二阶段——个人工作总结DAY07
    第二阶段——个人工作总结DAY06
    第二阶段——个人工作总结DAY05
    第二阶段——个人工作总结DAY04
    第二阶段——个人工作总结DAY03
    第十五周学习进度条
  • 原文地址:https://www.cnblogs.com/imakf/p/14428415.html
Copyright © 2011-2022 走看看