BZOJ5321 JXOI2017加法(二分答案+贪心+堆+树状数组)

时间:2023-03-09 16:07:19
BZOJ5321 JXOI2017加法(二分答案+贪心+堆+树状数组)

  二分答案后得到每个位置需要被加的次数。考虑贪心。从左到右考虑每个位置,将以该位置为左端点的区间按右端点从大到小加进堆。看该位置还需要被加多少次,如果不需要加了就不管,否则取堆顶区间将其选择,BIT实现区间覆盖。

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<queue>
#include<vector>
using namespace std;
#define ll long long
#define N 200010
char getc(){char c=getchar();while ((c<'A'||c>'Z')&&(c<'a'||c>'z')&&(c<''||c>'')) c=getchar();return c;}
int gcd(int n,int m){return m==?n:gcd(m,n%m);}
int read()
{
int x=,f=;char c=getchar();
while (c<''||c>'') {if (c=='-') f=-;c=getchar();}
while (c>=''&&c<='') x=(x<<)+(x<<)+(c^),c=getchar();
return x*f;
}
int T,n,m,k,a[N],c[N],tree[N];
priority_queue<int> q;
vector<int> b[N];
void add(int k,int x){while (k<=n) tree[k]+=x,k+=k&-k;}
int query(int k){int s=;while (k) s+=tree[k],k-=k&-k;return s;}
bool check()
{
for (int i=;i<=n;i++) tree[i]=;int cnt=;
while (!q.empty()) q.pop();
for (int i=;i<=n;i++) add(i,c[i]-c[i-]);
for (int i=;i<=n;i++)
{
for (int j=;j<b[i].size();j++) q.push(b[i][j]);
int x=query(i);
for (;x>;x--)
{
if (q.empty()) return ;
int y=q.top();q.pop();
if (y<i) return ;
add(i,-),add(y+,);
if ((++cnt)>k) return ;
}
}
return ;
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("bzoj5321.in","r",stdin);
freopen("bzoj5321.out","w",stdout);
const char LL[]="%I64d\n";
#else
const char LL[]="%lld\n";
#endif
T=read();
while (T--)
{
n=read(),m=read(),k=read();int p=read();
int l=,r,ans;
for (int i=;i<=n;i++) l=min(l,a[i]=read()),b[i].clear();
for (int i=;i<=m;i++)
{
int x=read(),y=read();
b[x].push_back(y);
}
r=l+m*p;
while (l<=r)
{
int mid=l+r>>;
for (int i=;i<=n;i++) c[i]=mid<=a[i]?:(mid-a[i]-)/p+;
if (check()) l=mid+,ans=mid;
else r=mid-;
}
printf("%d\n",ans);
}
return ;
}