[P4064][JXOI2017]加法(贪心+树状数组+堆)

题目描述

可怜有一个长度为 n 的正整数序列 A,但是她觉得 A 中的数字太小了,这让她很不开心。

于是她选择了 m 个区间 [li, ri] 和两个正整数 a, k。她打算从这 m 个区间里选出恰好 k 个区间,并对每个区间执行一次区间加 a 的操作。(每个区间最多只能选择一次。)

对区间 [l, r] 进行一次加 a 操作可以定义为对于所有 i ∈ [l, r],将 Ai 变成 Ai + k。现在可怜想要知道怎么选择区间才能让操作后的序列的最小值尽可能的大,即最大化min Ai

输入输出格式

输入格式:

第一行输入一个整数表示数据组数。

对于每组数据第一行输入四个整数 n, m, k, a。

第二行输入 n 个整数描述序列 A。

接下来 m 行每行两个整数 li, ri 描述每一个区间。数据保证所有区间两两不同。

输出格式:

对于每组数据输出一个整数表示操作后序列最小值的最大值。

输入输出样例

输入样例#1: 复制
1 
3 3 2 1
1 3 2
1 1
1 3
3 3
输出样例#1: 复制
3

说明

选择给区间 [1, 1] 和 [1, 3] 加 1。

对于100%的数据,保证1≤n,m≤200,1\leq n,m \leq 200, 1n,m200, 1≤T≤2000,1≤k≤m,1≤a≤100,1≤Ai≤1081\leq T\leq 2000, 1 ≤ k ≤ m, 1 ≤ a ≤ 100, 1 ≤ A_i ≤ 10^81T2000,1km,1a100,1Ai108

不要相信数据范围,应该是$1\leq n,m \leq 10^5$。

二分答案,贪心扫一遍,对于小于二分值的位置,用堆找到包含它且向右延伸最长的区间加上它,区间加用差分树状数组实现。

 1 #include<cstdio>
 2 #include<queue>
 3 #include<cstring>
 4 #include<algorithm>
 5 #define rep(i,l,r) for (int i=l; i<=r; i++)
 6 using namespace std;
 7 
 8 const int N=200100;
 9 priority_queue<int>Q;
10 int T,n,m,k,A,q[N],a[N],c[N],stk[N];
11 struct P{ int l,r; }p[N];
12 bool cmp(P a,P b){ return a.l<b.l; }
13 void add(int x,int k){ for (; x<=n+1; x+=x&-x) c[x]+=k; }
14 int que(int x){ int res=0; for (; x; x-=x&-x) res+=c[x]; return res; }
15 
16 bool chk(int lim){
17     while (!Q.empty()) Q.pop();
18     memset(c,0,sizeof(c)); int top=0,j=1,cnt=0;
19     rep(i,1,n) if (a[i]<lim) stk[++top]=i;
20     rep(i,1,top){
21         while (j<=m && p[j].l<=stk[i]) Q.push(p[j].r),j++;
22         while (a[stk[i]]+que(stk[i])<lim){
23             cnt++; if (cnt>k || Q.empty()) return 0;
24             int x=Q.top(); Q.pop(); add(stk[i],A); add(x+1,-A);
25         }
26     }
27     return 1;
28 }
29 
30 int main(){
31     freopen("P4064.in","r",stdin);
32     freopen("P4064.out","w",stdout);
33     for (scanf("%d",&T); T--; ){
34         scanf("%d%d%d%d",&n,&m,&k,&A);
35         rep(i,1,n) scanf("%d",&a[i]);
36         rep(i,1,m) scanf("%d%d",&p[i].l,&p[i].r);
37         sort(p+1,p+m+1,cmp);
38         int l=1,r=120000000,mid,ans;
39         while (l<=r){
40             mid=(l+r)>>1;
41             if (chk(mid)) ans=mid,l=mid+1; else r=mid-1;
42         }
43         printf("%d\n",ans);
44     }
45     return 0;
46 }

 

转载于:https://www.cnblogs.com/HocRiser/p/8806344.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值