模拟赛exam
【题目描述】
你很快地完成了所有的题目,并且使准确率达到了p/q,于是开始无所事事. 老师
看见了,对你说:“你今天做的题够多的了,别再做了. 我们接下来k 天每天都要模拟,这里有很多题,你去选一些题目编k 场模拟赛吧. ”老师将所有题目都编了序号,还给你了n 种出题方案(所有方案两两不同). 每种出题方案选择所有编号属于[l; r] 的题目,作为一天的试题. 现在,你需要选出k 种出题方案(一个方案只能选取一次),分别对应k 天的模拟赛. 老师非常强调复习的重要性,因此希望有一些题目每天都出现在模拟赛当中,你需要最大化每天都出现的题目的数量.
【输入格式】
从文件exam.in 中读入数据。
输入第一行包含两个正整数n; k (1 <=k<= n<= 3* 10^5),分别为出题方案数和模拟赛天数.接下来n 行,每行两个整数l; r (-10^9 <= l<= r<= 109),表示一个出题方案(保证所有方案两两不同).
【输出格式】
输出到文件exam.out 中。
输出一个非负整数,表示每天都出现的题目的数量的最大值.
【样例1 输入】
4 2
1 100
40 70
120 130
125 180
【样例1 输出】
31
【样例1 解释】
选择前两种方案,编号为[40; 70] 的题目在两种方案中均出现,共有31 道题.
【样例2 输入】
3 2
1 12
15 20
25 30
【样例2 输出】
0
【样例2 解释】
所有给出的方案互不相交,所以答案为0.
【样例3】
见选手目录下的exam/exam3.in 与exam/exam3.ans。
【子任务】
对10% 的数据,k = n;
对另20% 的数据,k = 2;
对另20% 的数据,1 <= k <= n <= 20;
其余5 组数据,n = 10^2; 10^3; 10^4; 10^5; 3 * 10^5.
因为要从n种方案里选出k个,所以不妨把每个区间先按左端点排序,选出前k-1个,同时维护一个越小的整数优先级越高的单调队列,把它们的右端点存进去。然后看第k个区间,如果它的左端点小于前k-1个区间最小的右端点,它就是合法的。这时还需要将它的右端点和前k-1个区间最小的右端点(以下称为当前最小右端点)比较,如果它的右端点小于当前最小右端点,则它是被包含的,题目数量就是它自己的左右端点相减+1;否则,用当前最小右端点与它的左端点相减+1得到sum。然后将当前最小右端点出队,将现在第k个区间的右端点入队,以此类推。那么如果第k个区间的左端点大于当前最小右端点,则现在的状态无法满足某道题被选了k次,这时就要不断将队首元素出队,将第k个区间右端点入队,直到达到合法的状态。
代码如下
1 #include<cstdio> 2 #include<algorithm> 3 #include<queue> 4 using namespace std; 5 #define ll long long int 6 int k,n; 7 ll sum,ans; 8 struct node 9 { 10 ll l,r; 11 }t[300005]; 12 ll cmp(node a,node b) 13 { 14 return a.l < b.l;//按左端点排序 15 } 16 priority_queue<ll,vector<ll>,greater<ll> > pq;//越小的整数优先级越高 17 int main() 18 { 19 scanf("%d%d",&n,&k); 20 for(int i = 1;i <= n;i++){ 21 scanf("%lld%lld",&t[i].l,&t[i].r); 22 } 23 sort(t+1,t+n+1,cmp); 24 for(int i = 1;i < k;i++) 25 { 26 pq.push(t[i].r);//优先队列中存右端点 27 } 28 for(int i = k;i <= n;i++) 29 { 30 if(t[i].l <= pq.top()) 31 { 32 if(t[i].r > pq.top()) 33 { 34 sum = pq.top() - t[i].l + 1; 35 pq.pop(); 36 pq.push(t[i].r); 37 } 38 else 39 { 40 sum = t[i].r - t[i].l + 1; 41 } 42 ans = max(ans,sum); 43 } 44 else 45 { 46 pq.pop(); 47 pq.push(t[i].r); 48 } 49 } 50 printf("%lld",ans); 51 return 0; 52 }