本题超坑爹,先是题意看了半个多小时都不理解样例,后来去找别人的题解才看懂了。
问题是,给k,q
k是火车最多能运载的人数,q是询问次数
q行 每行两个数a,b 指有一个人要从a站做到b站,是否合法
即,第一次假设列车是从1开始,一直走到尽头(1000000)
当火车遇到一个(a,b)时 ,先查询区间(a,b-1)的值是否小于k(表示列车在这段路上搭载少于k个人),若是,就把区间(a,b-1)的值加1,
如果查询结果大于k,则标明这时火车已经有k个人了,不能上车。
例如 k=2 先
给(1,3) 可以上 ,此时【1,3-1】区间标记为1 ,(为什么标记到区间b-1?因为在b点人已经下车,所以就没有加1)
给(2,3)可以上,此时【2,2】区间标记为1+1(之间为1) 同时把【2,2】区间以上的父区间都更新为子节点中较大的一个,即如果【2,2】区间已经为2,即满人了,那么【1,3】区间也要是满人了,但是【1,1】区间不属于【2,2】的父区间,仍然可以载人 ()
再给(1,3) 查询到 (1,3)区间的值为2=k,所以这个人不可以上
再给 (4,8) 这个区间是值为0,可以上
以上纯属举例示范,题中保证a<b ,不会有区间【2,2】;
用的办法自然是线段树的成段更新操作,只不过把 之前的每个节点记录他所代表的区间的区间和,变为,每个节点距离他所代表区间的最大值
也少不了延迟操作,避免超时嘛。
------------------------------------------------------------------
然而如此水的题我也TLE了几个小时,因为本人在vc上敲这个代码,然后vc不支持max()函数,只有个__max(),然后我就用了后者,一直超时,最后发现自己写个max,不用__max 就ac了
93MS | 18212K |
#include <cstdio>
#include <cmath>
#include <math.h>
#include <cstring>
#include <string>
#include <algorithm>
#include <iostream>
#include <queue>
#include <map>
#include <set>
#include <vector>
using namespace std;
int max(int a,int b)
{
return a>b?a:b;
}
const int N = 1000005 ;
int sum[4*N], add[4*N] ; //最极端是4N
void build(int l,int r,int i) // 线段树的建立;
{
add[i]=0;
sum[i]=0; // 用了lazy思想,提高了效率;
if(l==r) return;
int mid=(l+r)>>1;
build(l,mid,i<<1);
build(mid+1,r,i<<1|1);
}
void pushDown(int i, int l, int r) //把i节点的延迟标记传递到左右儿子节点
{
if(add[i] != 0)
{
// int mid = (l + r) >> 1;
add[i << 1] += add[i];
sum[i << 1] += add[i]; //[l, mid]代表左儿子区间
add[i << 1 | 1] += add[i];
sum[i << 1 | 1] += add[i]; //[mid + 1, r]代表右儿子区间
add[i] = 0;
}
}
void update(int i, int l, int r, int ql, int qr, int val) //更新区间为qlqr,当前区间为l,r,代表当前区间和的节点为i,更新值为val,
{
if(l > qr || ql > r) //更新区间不在当前区间内
return ;
if(l >= ql && r <= qr) //要更新的区间把当前区间完全包括,则把当前整个区间+val,然后返回上一层
{
sum[i] += val;
add[i] += val;
return ;
}
pushDown(i, l, r); //如果上面没reutrn 表示要往左右儿子区间查询,所以把延迟标记放下去
int mid = (l + r) >> 1;
update(i << 1, l, mid, ql, qr, val);
update(i << 1 | 1, mid + 1, r, ql, qr, val);
sum[i] = max(sum[i << 1] ,sum[i << 1 | 1]);
}
int query(int i, int l, int r, int ql, int qr) //查询区间为qlqr,当前区间为l,r,代表当前区间和的节点为i
{
if(l > qr || ql > r)
return 0;
if(l >= ql && r <= qr)
return sum[i];
pushDown(i, l, r); //同update
int mid =( l + r) >> 1;
return max(query(i << 1, l, mid, ql, qr)
, query(i << 1 | 1, mid + 1, r, ql, qr) );
}
int ans[N+2];
int tm[N+2][2];
int main()
{
int n,t,k ,a,b,i;
cin>>t;
int cnt=1;
while(t--)
{
int ok=0;
scanf("%d %d",&k,&n);
int maxx=0;
for( i = 1; i <= n; i++)
{
scanf("%d %d",&tm[i][0],&tm[i][1]);
maxx=max(maxx,tm[i][1]);
}
//memset(add,0,sizeof(add));
//memset(sum,0,sizeof(sum));
build(1,maxx,1); // 线段树的建立,本题中与直接memset效果一样
for( i = 1; i <= n; i++)
{
a=tm[i][0];
b=tm[i][1];
if (query(1,1,maxx,a,b-1)<k)
{
ans[++ok]=i;
update(1, 1, maxx, a, b-1, 1);
}
}
printf("Case %d:\n",cnt++);
for( i = 1; i <= ok; i++)
{
printf("%d ",ans[i]);
}
printf("\n\n");
}
return 0;
}