15000:[APIO2018]新家

目录

目录

本人的感受

网址

题目(老规矩用一本通的数据,试了好多遍真的弄不出来上标,不过上标的字母不太一样,实在不行可以通过链接看)

15000:[APIO2018]新家

【题目描述】

【输入】

【输出】

【输入样例】

【输出样例】

【提示】

分析

1. 数据预处理

•排序商店信息

•类型映射

2. 初始化结构

•分段树/区间树

3. 时间线遍历

•按年遍历

1.开业处理

2.关闭处理

4. 处理询问

•按坐标排序询问

•查询不方便指数

•对于每个坐标li(对不起还是没有标上去)的询问,遍历这些年份yi(同样抱歉),在对应类型的分段树中查询两点信息

1. 向左查询 

2. 向右查询

3. 不方便指数即为这两个距离中的较大值。

5. 输出结果

代码(C++版,其他语言的自己改)

总结

什么是APIO?


本人的感受

       虽然说时间给的比较足,但是还是很容易超时。一本通是过的,洛谷和LOJ应该也行。这题本身不算难也不需要多少数学基础,但是因为容易超时所以需要用算法进行优化。一本通上会等很久,但其实不超时:必须说这题在一本通里绝对是测试点第二多的(129个,能把人急死,第一多的有139个,还没做出来),目前前三快也要136秒,我的估计要450秒左右,不过只要在645秒之内就有可能不超时。

网址

一本通拓展题库:点击进入         一本通拓展题库15000:[APIO2018]新家:点击进入

洛谷题库:点击进入        洛谷P4632 [APIO2018] 新家:点击进入

BZOJ里好像没有但是我在一个叫LOJ的网站中看到了。LOJ:点击进入

LOJ#2585[APIO2018]新家:点击进入

题目(老规矩用一本通的数据,试了好多遍真的弄不出来上标,不过上标的字母不太一样,实在不行可以通过链接看)

15000:[APIO2018]新家


时间限制: 5000 ms         内存限制: 524288 KB
提交数: 34     通过数: 19

【题目描述】

五福街是一条笔直的道路,这条道路可以看成一个数轴,街上每个建筑物的坐标都可以用一个整数来表示。小明是一位时光旅行者,他知道在这条街上,在过去现在和未来共有 n𝑛 个商店出现。第 i𝑖 个商店可以使用四个整数 xi,ti,ai,bi𝑥𝑖,𝑡𝑖,𝑎𝑖,𝑏𝑖 描述,它们分别表示:商店的坐标、商店的类型、商店开业的年份、商店关闭的年份。

小明希望通过时光旅行,选择一个合适的时间,住在五福街上的某个地方。他给出了一份他可能选择的列表,上面包括了 q𝑞 个询问,每个询问用二元组(坐标,时间)表示。第 i𝑖 对二元组用两个整数 li,yi𝑙𝑖,𝑦𝑖 描述,分别表示选择的地点 li𝑙𝑖 和年份 yi𝑦𝑖。

现在,他想计算出在这些时间和地点居住的生活质量。他定义居住的不方便指数为:在居住的年份,离居住点最远的商店类型到居住点的距离。类型 t𝑡 的商店到居住点的距离定义为:在指定的年份,类型 t𝑡 的所有营业的商店中,到居住点距离最近的一家到居住点的距离。我们说编号为 i𝑖 的商店在第 y𝑦 年在营业当且仅当 ai≤y≤bi𝑎𝑖≤𝑦≤𝑏𝑖。注意,在某些年份中,可能在五福街上并非所有 k𝑘 种类型的商店都有至少一家在营业。在这种情况下,不方便指数定义为 −1。你的任务是帮助小明求出每对(坐标,时间)二元组居住的不方便指数。

【输入】

第一行包含三个整数 n𝑛 , k𝑘 和 q𝑞,分别表示商店的数量、商店类型的数量和(坐标,时间)二元组的数量。(1≤n,q≤3×105,1≤k≤n)(1≤𝑛,𝑞≤3×105,1≤𝑘≤𝑛)

接下来 n𝑛 行,每行包含四个整数 xi,ti,ai𝑥𝑖,𝑡𝑖,𝑎𝑖和bi𝑏𝑖 用于描述一家商店,意义如题面所述(1≤xi,ai,bi≤109,1≤ti≤k,ai≤bi)(1≤𝑥𝑖,𝑎𝑖,𝑏𝑖≤109,1≤𝑡𝑖≤𝑘,𝑎𝑖≤𝑏𝑖)

接下来 q𝑞 行,每行包含两个整数 li𝑙𝑖 ,和 yi𝑦𝑖 ,表示一组(坐标,时间)查询(1≤li,yi≤108)(1≤𝑙𝑖,𝑦𝑖≤108)

【输出】

对于每组询问输出一个整数,包含q𝑞个整数,依次表示对于 q𝑞 组(坐标,时间)询问求出的结果。

【输入样例】

4 2 4
3 1 1 10
9 2 2 4
7 2 5 7
4 1 8 10
5 3
5 6
5 9
1 10

【输出样例】

4
2
-1
-1

【提示】

样例输入2

2 1 3
1 1 1 4
1 1 2 6
1 3
1 5
1 7

样例输出2

0
0
-1

样例输入3

1 1 1
100000000 1 1 1
1 1

样例输出3

99999999

【样例解释】

在第一个样例中,有 4 家商店,共 2 种类型,还有 4 个询问。

对于第一个询问:小明在第 3 年住在坐标为 5 的地方。这一年中,编号为 1 和 2 的商店在营业,到编号为 1 的商店的距离为 2 ,到编号为 2 的商店距离为 4 ,所以最大距离为4。

对于第二个询问:小明在第 6 年住在坐标为 5 的地方。这一年中,编号为 1 和 3 的商店在营业,到编号为 1 的商店的距离为 2 ,到编号为 3 的商店距离为 2 ,所以最大距离为2。

对于第三个询问:小明在第 9 年住在坐标为 5 的地方。这一年中,编号为 1 和 4 的商店在营业,它们的类型都为 1,没有类型为 2 的商店在营业,所以答案为 −1 。

同样的情况出现在第四个询问中。

在第二个样例中,有 2 家商店,共 1 种类型,还有三个询问。 两家商店的类型都是 1 。在所有的询问中,小明均住在坐标为 1 的地方。 在前两个询问中,至少有一个商店在营业,所以答案为 0 ,在第三个询问中,两个商店都不在营业,所以答案为 −1 。

在第三个样例中,有 1 家商店和 1 个询问,两者之间的距离是 99999999 。

【数据范围与提示】

编译与评测:−O2−std=c++11−𝑂2−𝑠𝑡𝑑=𝑐++11

子任务 1(5 分):n,q≤400𝑛,𝑞≤400

子任务 2(7 分):n,q≤6×104,k≤400𝑛,𝑞≤6×104,𝑘≤400

子任务 3(10 分):n,q≤3×105𝑛,𝑞≤3×105,对于所有的商店ai=1,bi=108𝑎𝑖=1,𝑏𝑖=108

子任务 4(23 分):n,q≤3×105𝑛,𝑞≤3×105,对于所有的商店ai=1𝑎𝑖=1

子任务 5(35 分):n,q≤6×104𝑛,𝑞≤6×104

子任务 6(20 分):n,q≤3×105

分析

       为了解决这个问题,我们可以采用离线处理和分段查询的策略。首先,我们需要按照年份对商店信息进行排序,然后针对每种商店类型,使用分段树或平衡树(如Splay Tree、Segment Tree)来维护每一年每个坐标上最近的同类型商店距离。这样可以在O(logN)的时间复杂度内完成单点更新和区间查询操作。

1. 数据预处理

•排序商店信息

      首先,根据商店的开业年份ai对所有商店进行排序。这有助于我们按时间顺序处理商店的状态变化。

•类型映射

       创建一个映射表,用来存储每种商店类型t对应的商店列表。这有助于我们针对每种类型独立处理问题。

2. 初始化结构

•分段树/区间树

       为每种商店类型构建一个分段树,每个节点覆盖一段坐标范围,存储该范围内最靠近该坐标的营业商店的距离。初始时,树为空,因为尚未有任何商店开业。

3. 时间线遍历

•按年遍历

        从最早的开业年份开始,逐年遍历直到最晚的关闭年份。分两步处理:

1.开业处理

        对于每年新开业的商店,找到其所属的商店类型,将其坐标插入对应类型分段树的正确位置,更新距离信息。

2.关闭处理

        同时移除当年关闭的商店,从分段树中删除相应节点,重新计算区间最远距离。

4. 处理询问

•按坐标排序询问

        将所有询问按坐标li排序,这样可以连续处理同坐标的不同年份询问,减少重复计算。

•查询不方便指数

•对于每个坐标li(对不起还是没有标上去)的询问,遍历这些年份yi(同样抱歉),在对应类型的分段树中查询两点信息
1. 向左查询 

        找到最左边的营业商店,记录距离。

2. 向右查询

        找到最右边的营业商店,记录距离。

3. 不方便指数即为这两个距离中的较大值。

        如果查询不到商店(即该类型商店在该年未营业),标记结果为-1。

5. 输出结果

        按顺序输出每个询问的不方便指数。

代码(C++版,其他语言的自己改)

	
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define N 300100
using namespace std;

int n,m,Q,bb,cc,tb,tc,ss,tt,ans[N],b[N],cnt[N],cl;
void chg(int now,int l,int r,int u,int v);
struct Shop
{
    int cz,pos,type,ti;
    bool operator < (const Shop &u) const
    {
	if(ti!=u.ti) return ti<u.ti;
	return cz<u.cz;
    }
}shop[N*3];
struct Pq
{
    int tp,id;
    priority_queue<int,vector<int>,greater<int> >a,b;
    void cle(){for(;!b.empty() && a.top()==b.top();a.pop(),b.pop());}
    int top(){cle();return a.top();}
    void cg(){if(tp!=top()) tp=top(),chg(1,1,bb,id,tp);}
    void push(int u){a.push(u);if(id) cg();}
    void del(int u){b.push(u);if(id) cg();}
}pq[N],ri;
struct Node
{
    int ls,rs,mn;
}node[N<<1];
map<int,int>mb;
multiset<int>se[N];
multiset<int>::iterator it,i2;

inline void add(int u,int v)
{
    if(!cnt[v]) cl--;cnt[v]++;
    se[v].insert(u);
    i2=it=se[v].lower_bound(u);
    it++,i2--;
    pq[u].push(*i2);
    if(it!=se[v].end()) pq[*it].del(*i2),pq[*it].push(u);
    else ri.del(*i2),ri.push(u);
}

inline void del(int u,int v)
{
    cnt[v]--;if(!cnt[v]) cl++;
    i2=it=se[v].lower_bound(u);
    it++,i2--;
    pq[u].del(*i2);
    if(it!=se[v].end()) pq[*it].del(u),pq[*it].push(*i2);
    else ri.del(u),ri.push(*i2);
    se[v].erase(--it);
}

inline void up(int now){int L=node[now].ls,R=node[now].rs;node[now].mn=min(node[L].mn,node[R].mn);}
void build(int now,int l,int r)
{
    if(l==r)
    {
	node[now].mn=INF;
	return;
    }
    int mid=((l+r)>>1);
    node[now].ls=++tt;
    build(tt,l,mid);
    node[now].rs=++tt;
    build(tt,mid+1,r);
    up(now);
}

void chg(int now,int l,int r,int u,int v)
{
    if(l==r)
    {
	node[now].mn=v;
	return;
    }
    int mid=((l+r)>>1);
    if(u<=mid) chg(node[now].ls,l,mid,u,v);
    else chg(node[now].rs,mid+1,r,u,v);
    up(now);
}

int ask(int now,int l,int r,int u,int v)
{
    if(u<=l&&r<=v) return node[now].mn;
    int mid=((l+r)>>1);
    if(v<=mid) return ask(node[now].ls,l,mid,u,v);
    if(u>mid) return ask(node[now].rs,mid+1,r,u,v);
    return min(ask(node[now].ls,l,mid,u,v),ask(node[now].rs,mid+1,r,u,v));
}

inline bool judge(int u,int v)
{
    u=lower_bound(b+1,b+bb+1,u)-b;
    v=upper_bound(b+1,b+bb+1,v)-b-1;
    if(v<u) return 0;
    if(v<bb && ask(1,1,bb,v+1,bb)<u) return 0;
    if(ri.top()<u) return 0;
    return 1;
}

inline int calc(int u)
{
    if(cl) return -1;
    int l,r,mid;
    for(l=0,r=1e8;l<r;)
    {
	mid=((l+r)>>1);
	if(judge(u-mid,u+mid)) r=mid;
	else l=mid+1;
    }
    return l;
}

int main()
{
    int i,j,p,q,o,z;
    cin>>n>>m>>Q;cl=m;
    for(i=1;i<=m;i++) se[i].insert(0),ri.push(0);
    for(i=1;i<=n;i++)
    {
	scanf("%d%d%d%d",&z,&o,&p,&q);
	b[++tb]=z;
	shop[++ss].cz=1;
	shop[ss].pos=z;
	shop[ss].type=o;
	shop[ss].ti=p;
	shop[++ss].cz=3;
	shop[ss].pos=z;
	shop[ss].type=o;
	shop[ss].ti=q;
    }
    for(i=1;i<=Q;i++)
    {
	scanf("%d%d",&p,&q);
	shop[++ss].cz=2;
	shop[ss].pos=p;
	shop[ss].ti=q;
	shop[ss].type=i;
    }
    sort(b+1,b+tb+1);
    for(i=1;i<=tb;i++)
    {
	if(i==1 || b[i]!=b[i-1])
	{
	    b[++bb]=b[i];
	    mb[b[i]]=bb;
	}
    }
    for(i=1;i<=ss;i++) if(shop[i].cz!=2) shop[i].pos=mb[shop[i].pos];

    for(i=1;i<=bb;i++) pq[i].id=i,pq[i].push(INF);
    build(tt=1,1,bb);
    sort(shop+1,shop+ss+1);
    for(i=1;i<=ss;i=j)
    {
	z=shop[j=i].ti;
	for(;j<=ss&&shop[j].ti==z&&shop[j].cz==1;j++)
	    add(shop[j].pos,shop[j].type);
	for(;j<=ss&&shop[j].ti==z&&shop[j].cz==2;j++)
	    ans[shop[j].type]=calc(shop[j].pos);
	for(;j<=ss&&shop[j].ti==z&&shop[j].cz==3;j++)
	    del(shop[j].pos,shop[j].type);
    }
    for(i=1;i<=Q;i++) printf("%d\n",ans[i]);
}

总结

遇到这种子任务众多且没啥难度的题,基本上都会卡。一定要优化算法,制定策略。它给你时间限制只要是这种题就可以用来调整时间复杂度。

什么是APIO?

        我还真查了一下,APIO全称是Asia-Pacific Informatics Olympiad,,指亚洲与太平洋地区信息学奥林匹克竞赛。是一个面向亚太地区在校中学生的信息学学科竞赛.旨在给青少年提供更多的赛事机会,推动亚太地区的信息学奥林匹克的发展。该竞赛性质为区域性的网上准同步赛,在每年五月举办。APIO中国区由中国计算机学会主办。链接

  • 16
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值