zoukankan      html  css  js  c++  java
  • [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;
    }
    

  • 相关阅读:
    判断语句和循环语句2.2比较运算符
    判断语句和循环语句2.5 if中使用else
    判断语句和循环语句2.1 True、False
    判断语句和循环语句2.9while循环
    判断语句和循环语句2.7 if嵌套
    判断语句和循环语句2.8应用:猜拳游戏
    判断语句和循环语句逻辑运算符
    K8S 搭建 Prometheus (一) 部署 nodeexporter, prometheusserver
    使用Hive运行Job程序报GC错误
    离线数仓(四)
  • 原文地址:https://www.cnblogs.com/Troverld/p/14612782.html
Copyright © 2011-2022 走看看