[NOI2019] 弹跳

XLII.[NOI2019] 弹跳

一眼看上去,单点向矩阵连边、最短路,这不是数据结构优化建图是什么?

想了想二维线段树优化建图,发现可以。

于是就写了,内层线段树写的还是可以压缩空间的线段树合并。

然后MLE了。

\(88\) 分代码:

#include<bits/stdc++.h>
using namespace std;
int n,m,W,H,tot,rt[280100],id[70100],head[3001000],cnt,X[70100],Y[70100];
struct node{int to,next,val;}edge[10010000];
void ae(int u,int v,int w){edge[cnt].next=head[u],edge[cnt].to=v,edge[cnt].val=w,head[u]=cnt++;}
vector<int>v[70100];
#define mid ((l+r)>>1)
struct SegTree{int lson,rson;}seg[3001000];
void Innermodify(int &x,int fa,int l,int r,int P,int val){
	if(l>P||r<P)return;
	if(!x){x=++tot;if(fa)ae(fa,x,0);}
	if(l==r)id[val]=x;else Innermodify(seg[x].lson,x,l,mid,P,val),Innermodify(seg[x].rson,x,mid+1,r,P,val);
}
int Innermerge(int x,int y,int l,int r){
	if(!x||!y)return x+y;
	int z=++tot;
	if(l!=r){
		seg[z].lson=Innermerge(seg[x].lson,seg[y].lson,l,mid),seg[z].rson=Innermerge(seg[x].rson,seg[y].rson,mid+1,r);
		if(seg[z].lson)ae(z,seg[z].lson,0);if(seg[z].rson)ae(z,seg[z].rson,0);
	}else ae(z,x,0),ae(z,y,0);
	return z;
}
void Innerae(int x,int l,int r,int L,int R,int S,int T){
	if(l>R||r<L||!x)return;
	if(L<=l&&r<=R)ae(S,x,T);else Innerae(seg[x].lson,l,mid,L,R,S,T),Innerae(seg[x].rson,mid+1,r,L,R,S,T);
}
#define ls x<<1
#define rs x<<1|1
void Outerbuild(int x,int l,int r){
	if(l==r)for(auto i:v[l])Innermodify(rt[x],0,1,H,Y[i],i);else Outerbuild(ls,l,mid),Outerbuild(rs,mid+1,r),rt[x]=Innermerge(rt[ls],rt[rs],1,H);
}
void Outerae(int x,int l,int r,int OL,int OR,int IL,int IR,int S,int T){
	if(l>OR||r<OL)return;
	if(OL<=l&&r<=OR)Innerae(rt[x],1,H,IL,IR,S,T);else Outerae(ls,l,mid,OL,OR,IL,IR,S,T),Outerae(rs,mid+1,r,OL,OR,IL,IR,S,T);
}
int dis[3001000];
bool vis[3001000];
priority_queue<pair<int,int> >q;
void Dijkstra(int S){
	memset(dis,0x3f,sizeof(dis)),dis[S]=0,q.push(make_pair(-dis[S],S));
	while(!q.empty()){
		int x=q.top().second;q.pop();
		if(vis[x])continue;vis[x]=true;
		for(int i=head[x],y;i!=-1;i=edge[i].next)if(dis[y=edge[i].to]>dis[x]+edge[i].val)dis[y]=dis[x]+edge[i].val,q.push(make_pair(-dis[y],y));
	}
}
int main(){
//	freopen("I.in","r",stdin);
	scanf("%d%d%d%d",&n,&m,&W,&H),memset(head,-1,sizeof(head));
	for(int i=1;i<=n;i++)scanf("%d%d",&X[i],&Y[i]),v[X[i]].push_back(i);
	Outerbuild(1,1,W);
//	printf("%d\n",tot);
	for(int i=1,S,T,OL,OR,IL,IR;i<=m;i++)scanf("%d%d%d%d%d%d",&S,&T,&OL,&OR,&IL,&IR),Outerae(1,1,W,OL,OR,IL,IR,tot+S,T);
	for(int i=1;i<=n;i++)ae(id[i],tot+i,0);
	Dijkstra(tot+1);
	for(int i=2;i<=n;i++)printf("%d\n",dis[tot+i]);
	return 0;
}

想不到怎么优化。题解上说,内层可以用 set 替代线段树,这样空间复杂度会压到 \(O(n\log n)\)。并且,边也不用真的建出来——一个点向矩阵中所有点连边,就等 Dijkstra 遍历到该点时,将该点的所有矩阵也扔进大根堆里,若堆顶是点就执行上述操作,若堆顶是矩阵就更新矩阵内点。于是便照着题解思路写了,仍然MLE。

\(88\) 分代码:

#include<bits/stdc++.h>
using namespace std;
int n,m,W,H;
#define mid ((l+r)>>1)
#define lson x<<1
#define rson x<<1|1
set<pair<int,int> >s[280010];
struct Matrix{
	int id,l,r,d;
	Matrix(int I,int L,int R,int D){id=I,l=L,r=R,d=D;}
	friend bool operator<(const Matrix&u,const Matrix&v){return u.d>v.d;}
};//record a node linked to a matrix
vector<Matrix>v[70100];
void modify(int x,int l,int r,int X,int Y,int id){
	if(l>X||r<X)return;
	s[x].insert(make_pair(Y,id));
	if(l!=r)modify(lson,l,mid,X,Y,id),modify(rson,mid+1,r,X,Y,id);
}
void query(int x,int l,int r,int OL,int OR,int IL,int IR,int S,int T){
	if(l>OR||r<OL)return;
	if(OL<=l&&r<=OR)v[S].emplace_back(x,IL,IR,T);
	else query(lson,l,mid,OL,OR,IL,IR,S,T),query(rson,mid+1,r,OL,OR,IL,IR,S,T);
}
int dis[70100];
bool vis[70100];
priority_queue<Matrix>q;
void Dijkstra(){
	memset(dis,0x3f,sizeof(dis)),dis[1]=0,q.emplace(-1,-1,-1,0);
	while(!q.empty()){
		Matrix x=q.top();q.pop();
		if(x.id>0){
			auto i=s[x.id].lower_bound(make_pair(x.l,0)),j=s[x.id].upper_bound(make_pair(x.r,0x3f3f3f3f));
			for(auto k=i;k!=j;k=s[x.id].erase(k))if(dis[k->second]>x.d)dis[k->second]=x.d,q.push(Matrix(-k->second,-1,-1,dis[k->second]));
			continue;
		}
		int X=-x.id;
		if(vis[X])continue;vis[X]=true;
		for(auto i:v[X])q.push(Matrix(i.id,i.l,i.r,i.d+dis[X]));
	}
}
int main(){
	scanf("%d%d%d%d",&n,&m,&W,&H);
	for(int i=1,x,y;i<=n;i++)scanf("%d%d",&x,&y),modify(1,1,W,x,y,i);
	for(int i=1,S,T,OL,OR,IL,IR;i<=m;i++)scanf("%d%d%d%d%d%d",&S,&T,&OL,&OR,&IL,&IR),query(1,1,W,OL,OR,IL,IR,S,T);
	Dijkstra();
	for(int i=2;i<=n;i++)printf("%d\n",dis[i]);
	return 0;
}

后来没办法,看了题解代码。原来,题解并没有像我一样预先将矩阵在线段树上拆分,而是等处理到一个矩阵时再把它扔进线段树上处理。同时,题解中大根堆里只存储了未拆分的矩阵,这部分空间复杂度就是 \(O(m)\) 而非我上面的 \(O(m\log n)\) 了(可能就是这边让我上面的代码挂掉了)。存储未拆分的矩阵;更新矩阵内部点时,直接将点上矩阵扔进堆中;更新完矩阵内部点就直接在整棵树上删掉这些点……就是这种精打细算的压缩空间,才没有被卡掉。

代码:

#include<bits/stdc++.h>
using namespace std;
int n,m,W,H,X[70100],Y[70100];
#define mid ((l+r)>>1)
#define lson x<<1
#define rson x<<1|1
set<pair<int,int> >s[280010];
struct Matrix{
	int l,r,L,R,d;
	Matrix(int a,int b,int C,int D,int e){l=a,r=b,L=C,R=D,d=e;}
	friend bool operator<(const Matrix&u,const Matrix&v){return u.d>v.d;}
};
vector<Matrix>v[70100];
void modify(int x,int l,int r,int id){
	if(l>X[id]||r<X[id])return;
	s[x].insert(make_pair(Y[id],id));
	if(l!=r)modify(lson,l,mid,id),modify(rson,mid+1,r,id);
}
void yfidom(int x,int l,int r,int id){
	if(l>X[id]||r<X[id])return;
	s[x].erase(make_pair(Y[id],id));
	if(l!=r)yfidom(lson,l,mid,id),yfidom(rson,mid+1,r,id);
}
int dis[70100];
bool vis[70100];
priority_queue<Matrix>q;
void solve(int x,int l,int r,Matrix M){
	if(l>M.r||r<M.l)return;
	if(M.l<=l&&r<=M.r){
		auto i=s[x].lower_bound(make_pair(M.L,0)),j=s[x].upper_bound(make_pair(M.R,0x3f3f3f3f));
		vector<int>tmp;
		for(auto k=i;k!=j;k++){
			dis[k->second]=M.d,tmp.push_back(k->second);
			for(auto o:v[k->second])o.d+=M.d,q.push(o);
		}
		for(auto o:tmp)yfidom(1,1,W,o);
	}else solve(lson,l,mid,M),solve(rson,mid+1,r,M);
}
void Dijkstra(){
	for(auto i:v[1])q.push(i);
	yfidom(1,1,W,1);
	while(!q.empty()){
		auto M=q.top();q.pop();
		solve(1,1,W,M);
	}
}
int main(){
	scanf("%d%d%d%d",&n,&m,&W,&H);
	for(int i=1,x,y;i<=n;i++)scanf("%d%d",&X[i],&Y[i]),modify(1,1,W,i);
	for(int i=1,S,T,OL,OR,IL,IR;i<=m;i++)scanf("%d%d%d%d%d%d",&S,&T,&OL,&OR,&IL,&IR),v[S].emplace_back(OL,OR,IL,IR,T);
	Dijkstra();
	for(int i=2;i<=n;i++)printf("%d\n",dis[i]);
	return 0;
}

posted @ 2021-04-02 23:11  Troverld  阅读(55)  评论(0编辑  收藏  举报