zoukankan      html  css  js  c++  java
  • CodeForces

    (sf{Description})

    传送门

    (sf{Solution})

    ( ext{Subtask 1})

    保证 (m=1)

    定义 (f[i][j]) 为前 (i) 棵竹子砍了 (j) 次剩下的最高竹子的最小值,就有一个 (mathcal O(nk^2))(mathtt{dp})

    [f[i][j]=min{max{f[i-1][k],h[i]+a[i]-(j-k) imes p}} ]

    ( ext{Subtask 2})

    最大值最小应该想到二分。

    因为有竹子会砍没的情况,所以不好贪心。

    正难则反(话说自己根本想不到好吗),倒着考虑:一开始竹子高度全为 ( m mid),每天竹子会长矮 (a_i),你可以选择 (k) 个拔高 (p),要求 过程中 不能 (< 0) 且最终所有竹子均 (geq h_i)(倒着来就是先长矮再拔高)。

    为什么不能 (< 0)?这相当于从土地里面长出来,意味着高度被砍成负数。所以当竹子高度 (<0) 时,设此时刻 加一 的时刻为 (t),我们必须在 ([t,m]) 时刻区间内将竹子拔高。另外,从这里也可以看出越后面的拔高次数越珍贵,我们需要尽量拖延到 "非拔不可" 的时候拔高。所以每棵竹子的生长函数也是 唯一 的。

    为什么保证最终所有竹子均 (geq h_i)?可以画一张图,将天数为 (x) 坐标,将高度为 (y) 坐标,把满足以上条件的 (i) 竹子的生长函数画出来(还要保证以 ((m, m mid)) 为终点)。如果生长函数的初始位置 ((0,y))(y<h_i),就需要将整个函数向上平移到 ((0,h_i)) 才是这棵竹子的生长轨迹,这样你会发现它最终比 ( m mid) 高了,这是不合法的。 所以,在计算出生长函数后,我们需要再拔高竹子使得 (yge h_i),这时使用的拔高次数就可以使用 (m) 天中的任何一天了。

    你可能会疑惑。我又 tm 在这里搞了很久,考场上硬是打了一半没打了。

    为什么计算出每棵竹子的生长函数后,可以用这个生长函数来生长这棵竹子呢?毕竟,保证了 (yge h_i),也就意味着将生长函数应用在这棵竹子上,竹子的生长轨迹是生长函数向下平移的图像。而题目中要求砍竹子时要和 (0)(max),不会出问题吗?事实上,当把竹子砍成 (0) 时,我们就让它变成 (0),这肯定比同样时刻生长函数的 (y) 坐标更优了!

    具体实现时,可以朴素地 (mathcal O(nmlog V)) 来计算。更好的解法是将 (n) 棵竹子的 (m) 天都放在一起算:因为总共砍伐次数只有 (mk),开 (m) 个队列,第 (i) 个队列记录第 (m-i+2) 天必须砍的竹子。这是 (mathcal O((n+mk)cdot log V)) 的。

    (sf{Code})

    #include <cstdio>
    
    #define rep(i,_l,_r) for(register signed i=(_l),_end=(_r);i<=_end;++i)
    #define fep(i,_l,_r) for(register signed i=(_l),_end=(_r);i>=_end;--i)
    #define print(x,y) write(x),putchar(y)
    
    typedef long long ll;
    
    template <class T> inline T read(const T sample) {
        T x=0; int f=1; char s;
        while((s=getchar())>'9'||s<'0') if(s=='-') f=-1;
        while(s>='0'&&s<='9') x=(x<<1)+(x<<3)+(s^48),s=getchar();
        return x*f;
    }
    template <class T> inline void write(const T x) {
        if(x<0) return (void) (putchar('-'),write(-x));
        if(x>9) write(x/10);
        putchar(x%10^48);
    }
    
    #include <queue>
    #include <iostream>
    using namespace std;
    typedef pair <int,int> Pair;
    
    const int maxn=1e5+5,maxm=5005;
    
    int n,m,k,p,h[maxn],a[maxn];
    ll ans;
    queue <Pair> q[maxm]; 
    
    bool ok(ll x) {
    	ll t; Pair u;
    	rep(i,1,m) while(!q[i].empty()) q[i].pop();
    	rep(i,1,n) {
    		t=x/a[i]+1;
    		if(t<=m) q[t].push(make_pair(i,0));
    	}
    	for(int i=1,Cut=0;i<=m;++i,Cut+=k) {
    		while(!q[i].empty()) {
    			if(Cut) --Cut;
    			else return 0;
    			u=q[i].front(); q[i].pop(); ++u.second;
    			t=(x+1ll*p*u.second)/a[u.first]+1;
    			if(t<=m) q[t].push(u);
    		}
    	}
    	t=0;
    	rep(i,1,n)
    		t+=(max(0ll,1ll*a[i]*m+h[i]-x)+p-1)/p;
    	return t<=1ll*m*k;
    }
    
    int main() {
    	n=read(9),m=read(9),k=read(9),p=read(9);
    	rep(i,1,n) h[i]=read(9),a[i]=read(9);
    	ll l=0,r=0,mid;
    	rep(i,1,n) r=max(r,1ll*a[i]*m+h[i]);
    	while(l<=r) {
    		mid=l+r>>1;
    		if(ok(mid)) ans=mid,r=mid-1;
    		else l=mid+1;
    	}
    	print(ans,'
    ');
    	return 0;
    }
    
  • 相关阅读:
    gitolite 丢失管理密钥/访问权限 解决办法
    4/20
    socket套接字模块
    网络编程part2
    网络编程part1
    异常处理
    类的属性查找
    多继承带来的菱形问题
    property装饰器
    类的继承派生
  • 原文地址:https://www.cnblogs.com/AWhiteWall/p/14072547.html
Copyright © 2011-2022 走看看