二分答案后得到每个位置需要被加的次数。考虑贪心。从左到右考虑每个位置,将以该位置为左端点的区间按右端点从大到小加进堆。看该位置还需要被加多少次,如果不需要加了就不管,否则取堆顶区间将其选择,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<'0'||c>'9')) c=getchar();return c;} int gcd(int n,int m){return m==0?n:gcd(m,n%m);} int read() { int x=0,f=1;char c=getchar(); while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();} while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),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=0;while (k) s+=tree[k],k-=k&-k;return s;} bool check() { for (int i=1;i<=n;i++) tree[i]=0;int cnt=0; while (!q.empty()) q.pop(); for (int i=1;i<=n;i++) add(i,c[i]-c[i-1]); for (int i=1;i<=n;i++) { for (int j=0;j<b[i].size();j++) q.push(b[i][j]); int x=query(i); for (;x>0;x--) { if (q.empty()) return 0; int y=q.top();q.pop(); if (y<i) return 0; add(i,-1),add(y+1,1); if ((++cnt)>k) return 0; } } return 1; } int main() { #ifndef ONLINE_JUDGE freopen("bzoj5321.in","r",stdin); freopen("bzoj5321.out","w",stdout); const char LL[]="%I64d "; #else const char LL[]="%lld "; #endif T=read(); while (T--) { n=read(),m=read(),k=read();int p=read(); int l=100000000,r,ans; for (int i=1;i<=n;i++) l=min(l,a[i]=read()),b[i].clear(); for (int i=1;i<=m;i++) { int x=read(),y=read(); b[x].push_back(y); } r=l+m*p; while (l<=r) { int mid=l+r>>1; for (int i=1;i<=n;i++) c[i]=mid<=a[i]?0:(mid-a[i]-1)/p+1; if (check()) l=mid+1,ans=mid; else r=mid-1; } printf("%d ",ans); } return 0; }