题目描述:
1631 小鲨鱼在51nod小学
鲨鱼巨巨2.0(以下简称小鲨鱼)以优异的成绩考入了51nod小学。并依靠算法方面的特长,在班里担任了许多职务。
每一个职务都有一个起始时间A和结束时间B,意为小鲨鱼在[A, B]时间内,担任了某职务(inclusively)。
现在给定小鲨鱼的职务履历表,你可以高效的给出小鲨鱼在某天担任了哪些职务吗?
p.s. 由于小鲨鱼担任的职务太多,所有任期小于一个自然月的职务都忽略不计。(如1月1日~2月1日为一个自然月,即月份加1)
p.p.s. 输入数据保证小鲨鱼同时不担任超过200种职务。(牛!)
p.p.p.s 输入的日期均为合法日期,范围在2000年01月01日~2999年12月31日。
输入
第一行为一个整数n,代表小鲨鱼担任过N种职务。(1 <= n <= 10^5) 接下来的n行,每一行为七个整数,y0, m0, d0, y1, m1, d1, x。意为在<y0, m0, d0>到<y1, m1, d1>时间内,小鲨鱼担任了职务x。(1 <= x <= 10^9) 给定的时间皆合法,且起始日期小于或等于截止日期。职务x是唯一的。 接下来是一个整数q,代表q次查询。(1 <= q <= 10^4) 接下来的q行,每一行为三个整数<y, m, d>,代表查询的日期。时间皆合法。输出
每一次查询输出一行结果。 首先输出一个整数n,代表此时小鲨鱼担任的职务数。(n可以为0) 接下来是n个整数,代表小鲨鱼担任的职务。职务列表保持升序。输入样例
4 2000 01 01 2000 01 01 111 2000 01 02 2001 02 02 222 2000 01 28 2000 02 29 333 2000 01 29 2000 02 28 444 4 2000 01 01 2000 01 02 2000 01 28 2000 02 29输出样例
0 1 222 2 222 333 2 222 333
思路描述:
乍一看离线查询,但实际上按时间排序后假如职务1被查询1和查询2占用,到了职务2时,
依旧可能被查询3之前几个查询占用;假设换一种策略,先枚举查询再枚举职务,面临同样的
回溯问题,所以离线查询不可行。
正解在2000~2999年的每一天上建立线段树,每一次设定职务即在树上作区间修改,每一次查询
即单点查询。区间修改+单点查询的线段树是不用下传懒标记什么的,当修改区间完全覆盖某点时,
修改该点并返回,单点查询时只要将从根节点一直到目标叶子节点上的附加值全部相加即可,本体
附加值换成了一个容器。
PS:为方便在线段树上进行区间操作,用 的散列
函数将输入时间简化。
代码实现:
#include<iostream>
#include<cstring>
#include<cmath>
#include<queue>
#include<algorithm>
#define ll long long
#define INF 0x3f3f3f3f
using namespace std;
const int N=2e6+100;
int dateHash(int y,int m,int d){
return (y-2000)*372+(m-1)*31+d;
}
int Left[N],Right[N];
vector<int>dat[N],res;
void build(int p,int l,int r){
Left[p]=l;
Right[p]=r;
dat[p].clear();//利用建树的过程清空
if(l==r)return ;
int mid=(l+r)/2;
build(2*p,l,mid);
build(2*p+1,mid+1,r);
}
void change(int p,int l,int r,int v){
if(l<=Left[p]&&r>=Right[p]){ //针对单点查询的区间修改
dat[p].push_back(v);
return ;
}
int mid=(Left[p]+Right[p])/2;
if(l<=mid)change(2*p,l,r,v);
if(r>mid)change(2*p+1,l,r,v);
}
int query(int p,int x){ //查询要一直搜到叶子节点
if(Left[p]==Right[p]){
res.insert(res.end(),dat[p].begin(),dat[p].end());
return dat[p].size();
}
int mid=(Left[p]+Right[p])/2,val=0;
if(x<=mid)val=query(2*p,x);
else val=query(2*p+1,x);
res.insert(res.end(),dat[p].begin(),dat[p].end());//容器叠加
return dat[p].size()+val;//本题的输入确保了同一路径上不同节点相同职务不会重复存
}
int main(){
int n,q,y,m,d,yb,mb,db,x;
while(cin>>n){
memset(Left,0,sizeof(Left));
memset(Right,0,sizeof(Right));
build(1,1,dateHash(2999,12,31));
for(int i=1;i<=n;i++){
cin>>y>>m>>d;
cin>>yb>>mb>>db>>x;
if(dateHash(yb,mb,db)-dateHash(y,m,d)<31)continue;
change(1,dateHash(y,m,d),dateHash(yb,mb,db),x);
}
cin>>q;
while(q--){
res.clear();
cin>>y>>m>>d;
cout<<query(1,dateHash(y,m,d))<<" ";
sort(res.begin(),res.end());
for(vector<int>::iterator it=res.begin();it!=res.end();it++){
cout<<*it<<" ";
}
cout<<endl;
}
}
return 0;
}
THE END;