DLUTOJ -1234: Zeratul与塔防游戏(二分+线段树+贪心)

题解

维护长为m的树状数组,先将n次区间修改维护到数组上。

二分答案为q,每次判断需要升级的次数,是否小于k。

我们从左到右遍历塔i,类似manacher/扩展kmp算法一样更新一个当前最右端点nowr,

其实是贪心的思想,代表当前存在一个防御塔能更新到nowr,

对于不需要更新的点i,跳过即可;

需要更新点i的时候,我们就对[i,nowr]区间进行区间更新,显然是最优的。

最大化最小值,二分经典题型,就是check(mid)需要动一动脑筋。

代码

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long ll;
const int maxn=1e4+10;
ll bit0[maxn],bit1[maxn],tmp0[maxn],tmp1[maxn],n,m,k;
int range[maxn],nowr;
int lowbit(int x)
{
  return x&(-x);
}
void add(ll *b,int i,ll x)
{
    while(i<=m)
    {
     b[i]+=x;
     i+=lowbit(i);
    }
}
ll sum(ll *b,int i)
{
   ll ans=0;
   while(i>0)
   {
    ans+=b[i];
    i-=lowbit(i);
   }
   return ans;
}
ll ask(ll *bit1,ll *bit0,int l,int r)
{
   ll res=0;
   res+=sum(bit0,r)+sum(bit1,r)*r;
   res-=sum(bit0,l-1)+sum(bit1,l-1)*(l-1);
   return res;
}
void jia(ll *bit1,ll *bit0,int l,int r,ll a)
{
     add(bit0,l,-a*(l-1));
     add(bit1,l,a);
     add(bit0,r+1,a*r);
     add(bit1,r+1,-a);
}
bool check(ll mid)
{
    ll num=0;
    nowr=0;
    for(int i=1;i<=m;++i)
    {
        tmp0[i]=bit0[i];
        tmp1[i]=bit1[i];
    }
    for(int i=1;i<=m;++i)
    {
        ll q=ask(tmp1,tmp0,i,i);
        nowr=max(nowr,range[i]);//当前能到的最右 
        if(q>=mid)continue;
        if(nowr<i)return 0;
        num+=mid-q;
	    jia(tmp1,tmp0,i,nowr,mid-q);
        if(num>k)return 0;
    }
    return 1;
}
ll erfen(ll l,ll r)
{
    while(r-l>1)
    {
        ll mid=(l+r)/2;
        if(check(mid))l=mid;
        else r=mid;
    }
    return l;
}
int main()
{
  scanf("%lld%lld%lld",&n,&m,&k);
  for(int i=0;i<n;++i)
  {
     int l,r,a;
     scanf("%d%d%d",&l,&r,&a);
     jia(bit1,bit0,l,r,a);
     range[l]=max(range[l],r);
  }
  printf("%lld\n",erfen(0,1e18));
  return 0;
}

猜你喜欢

转载自blog.csdn.net/Code92007/article/details/83961712