【题目】
原题地址
题意:
一个二维平面,给定一个初始平台和 n n n个能量平台,均平行 x x x轴。
有两种能量发射器:
t y p 1 typ1 typ1:若安装在平台上方,向右上45度发射,在下方则向右下45度发射
t y p 2 typ2 typ2:同时向右上和右下45度发射
当能量发射器能量碰到一个平台时,会激活这个平台上的能量发射器,发射器在一次行动中不能重复激活。
每个能量平台至多有一个能量发射器。初始平台上每个整点都有一个能量发射器。
给定一个竖直线段,定义初始平台上每个能量发射器带来的收益为所有最终到达竖直线段上的路径经过所有平台的和。求前 k k k大收益。
【解题思路】
由于平台不能重复激活,平台之间的关系构成了一个DAG,我们如果暴力建图是 O ( n 3 ) O(n^3) O(n3)的,而建出图后用类似记忆化搜索的方式可以做到接近 O ( k ) O(k) O(k)。
我们发现建图时,每个点只会连出去最多两条,所以我们将平台按照 y y y的大小排序,然后 n 2 n^2 n2的就可以建完图了,同样的搜一遍,取前 k k k大的加入答案即可。
接着优化建图,我们可以将每个平台拆为若干点,然后对平台按照 y y y和 x x x排序后直接用直线函数求交点,直接建图,当每个平台(除了开始平台和飞船)建边超过2条时(这两条要合法),就不用再建了。
j建图的复杂度为 O ( ∑ 平 台 长 度 ) O(\sum 平台长度) O(∑平台长度)
有的点太多了,所以要将其缩为一个点,所以将其他平台全部看成一个点。
我们发现只有两种边,其实最后建出来的边只有最多 2 n 2n 2n条。
所以我们使用扫描线和平衡树辅助建边,将复杂度降为 n l o g n nlogn nlogn。
这里我们要用斜率为1和-1的扫描线来赛,可以分别按照第一关键字为 x + y x+y x+y和 x − y x-y x−y第二关键字为 y y y对点排序,而平衡树用 y y y为第一关键字 x + y x+y x+y和 x − y x-y x−y为第二关键字。
然后将一个平台拆成入点出点和发射点,三个点。
开始平台则拆为 x 2 − x 1 + 1 x2-x1+1 x2−x1+1个点,飞船拆为两个点。
然后对点排序,做两次扫描线建图,使用记忆化搜索即可拿到全部的分。
平衡树可以用set。
【参考代码】
#include
using namespace std;
typedef long long ll;
const int N=5e6+10;
int n,m,K,xl,xr,wx,wy,yl,yr,V;
int tot,CMP,top,ed,cnt,head[N];
bool in[N];
ll ans,INF,val[N],rec[N],cst[N],res[N];
int read()
{
int ret=0;char c=getchar();
while(!isdigit(c)) c=getchar();
while(isdigit(c)) ret=ret*10+(c^48),c=getchar();
return ret;
}
struct Tway{int v,nex;}e[N<<2];
void add(int u,int v){e[++top]=(Tway){v,head[u]};head[u]=top;}
struct Tpoint
{
int x,y,id,typ,upd;
Tpoint(){}
Tpoint(int x,int y,int id,int typ,int upd):x(x),y(y),id(id),typ(typ),upd(upd){}
bool operator < (const Tpoint&a) const
{
if(CMP) return y^a.y?ya.y:x+ys;
multiset::iterator it;
//void write(Tpoint a){printf("%d %d %d %d %d\n",a.x,a.y,a.id,a.typ,a.upd);}
void addline(int id)
{
int typ=read(),lx=read(),rx=read(),y=read();
p[++tot]=Tpoint(lx,y,id,1,-1);p[++tot]=Tpoint(rx,y,id,-1,-1);
if(typ&1)
{
int pos=read(),w=read(),upd=read();
val[id]=w;p[++tot]=Tpoint(pos,y,id,0,upd);
}
else if(typ&2)
{
int pos=read(),w=read();
val[id]=w;p[++tot]=Tpoint(pos,y,id,0,2);
}
}
void init()
{
K=read();n=read();xl=read();xr=read();wy=read();
V=read();wx=read();yl=read();yr=read();
for(int i=1;i<=n;++i) addline(i);
memset(rec,0x3f,sizeof(rec));INF=rec[0];
}
void tryadd(const Tpoint&a)
{
s.insert(a); it=s.find(a);
if(it==s.begin()) return;
for(--it;;--it)
{
Tpoint t=*it;
if(in[t.id] && t.id^a.id){add(a.id,t.id);break;}
else if(!in[t.id]) s.erase(it),it=s.find(a);
if(it==s.begin()) break;
}
}
namespace S1
{
bool cmp(const Tpoint&a,Tpoint &b)
{
if(a.x-a.y==b.x-b.y)
{
if(a.y==b.y || a.typ^b.typ) return a.typ>b.typ;
return a.y>b.y;
}
else return a.x-a.yb.typ;
return a.y