DLUTOJ -1234: Zeratul與塔防遊戲(二分+線段樹+貪心)
阿新 • • 發佈:2018-12-03
題解
維護長為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; }