luogu P5471 [NOI2019] 弹跳
题面传送门
这个可真的难写,几个月前就想出来怎么做了直到今天才花了3个小时写掉。
首先显然可以树套树优化建图达到\(O(nlog^2n)\)的时空复杂度。
但是发现这个居然卡空间。
所以我们可以不把边建出来而模拟dijkstra的过程。
我们只需要维护三个操作:删除一个点,给一个区间取\(min\),求全局最小值。显然树套树可做。
然后我兴高采烈地敲了一个线段树套fhq-treap上去。
code:
#include<bits/stdc++.h>
#define I inline
#define N 70039
#define min(a,b) ((a)<(b)?(a):(b))
#define l(x) x<<1
#define r(x) x<<1|1
using namespace std;
int n,m,k,x[N],y[N],z,d[N],root[8*N],w,h;
struct ques{int t,a,b,c,d;}tmp;
vector<ques> g[N];
struct pai{
int w,id;
pai operator +(const pai &s)const{return w<s.w?(pai){w,id}:s;}
}f[8*N],now;
struct yyy{
int x,y;
bool operator <=(const yyy &s)const{return (x==s.x)?(y<=s.y):(x<s.x);}
};
struct fhq{
int l[N*20],r[N*20],sum[N*20],key[N*20],cnt,root1,root2,root3;pai f[N*20],val[N*20];yyy id[N*20];
I int newnode(pai x,yyy ids){++cnt;id[cnt]=ids;f[cnt]=val[cnt]=x;sum[cnt]=2e9;key[cnt]=rand();return cnt;}
I void up(int x){f[x]=f[l[x]]+f[r[x]]+val[x];}
I void push(int x,int y){x&&(f[x].w=min(f[x].w,y),val[x].w=min(val[x].w,y),sum[x]=min(sum[x],y));}
I void pushdown(int x){sum[x]<2e9&&(push(l[x],sum[x]),push(r[x],sum[x]),sum[x]=2e9);}
I void split(yyy x,int now,int &a,int &b){
if(!now)return (void)(a=b=0);pushdown(now);
id[now]<=x?(a=now,split(x,r[now],r[now],b)):(b=now,split(x,l[now],a,l[now]));up(now);
}
I int merge(int x,int y){
if(!x||!y) return x|y;pushdown(x);pushdown(y);
return key[x]<key[y]?(r[x]=merge(r[x],y),up(x),x):(l[y]=merge(x,l[y]),up(y),y);
}
I void insert(yyy id,int &root,pai x){split(id,root,root1,root2);root=merge(root1,merge(newnode(x,id),root2));}
I void del(yyy id,int &root){
id.y--;split(id,root,root1,root2);
id.y++;split(id,root2,root2,root3);
root=merge(root1,root3);}
I void get(yyy x,yyy y,int z,int &root){
if(z>=sum[root]) return;
split(x,root,root1,root2);
split(y,root2,root2,root3);
push(root2,z);
root=merge(merge(root1,root2),root3);}
}s;
I void up(int x){f[x]=f[l(x)]+f[r(x)]+s.f[root[x]];}
I void get(int x,int y,int id,int l=1,int r=w,int now=1){
s.insert((yyy){y,x},root[now],(pai){(id^1)?2e9:0,id});if(l==r)return (void)(up(now));
int m=l+r>>1;x<=m?(get(x,y,id,l,m,l(now))):(get(x,y,id,m+1,r,r(now)));up(now);
}
I void del(int x,int y,int l=1,int r=w,int now=1){
s.del((yyy){y,x},root[now]);if(l==r) return (void)(up(now));
int m=l+r>>1;x<=m?(del(x,y,l,m,l(now))):(del(x,y,m+1,r,r(now)));up(now);
}
I void gets(int x,int y,int a,int b,int z,int l=1,int r=w,int now=1){
if(x<=l&&r<=y){s.get((yyy){a,0},(yyy){b,1e9},z,root[now]);return (void)(up(now));}int m=l+r>>1;
(x<=m)&&(gets(x,y,a,b,z,l,m,l(now)),0);(y>m)&&(gets(x,y,a,b,z,m+1,r,r(now)),0);up(now);
}
I void read(int &x){
char s=getchar();x=0;
while(s<'0'||s>'9') s=getchar();
while(s>='0'&&s<='9') x=x*10+s-48,s=getchar();
}
int main(){
// freopen("1.in","r",stdin);freopen("1.out","w",stdout);
srand(51569);register int i,j;scanf("%d%d%d%d",&n,&m,&w,&h);memset(d,0x7f,sizeof(d));
for(i=1;i<8*N;i++) f[i].w=2e9;s.f[0].w=2e9;
for(i=1;i<=n;i++) read(x[i]),read(y[i]),get(x[i],y[i],i);
for(i=1;i<=m;i++) read(z),read(tmp.t),read(tmp.a),read(tmp.b),read(tmp.c),read(tmp.d),g[z].push_back(tmp);//return 0;
for(i=1;i<=n;i++){
now=f[1];d[now.id]=now.w;del(x[now.id],y[now.id]);
for(j=0;j<g[now.id].size();j++)
tmp=g[now.id][j],gets(tmp.a,tmp.b,tmp.c,tmp.d,tmp.t+now.w)/*,printf("%d %d %d\n",now.id,tmp.a,tmp.b)*/;
}
for(i=2;i<=n;i++) printf("%d\n",d[i]);
}
然后悲惨地T了。
仔细分析一下发现这个其实是\(O(nlogn+mlog^2n)\)的。
然后发现\(m\)比\(n\)大很多。思考一下能不能把\(log^2n\)转移到\(n\)这边来。
我们可以开一个堆维护当前所有矩形,然后每到一个矩形就看这个矩形内没有被删除的点并拓展。这样就可以达到\(O(nlog^2n+mlogn)\)且常数小很多。
code:
#include<bits/stdc++.h>
#define I inline
#define N 70039
#define min(a,b) ((a)<(b)?(a):(b))
#define l(x) x<<1
#define r(x) x<<1|1
using namespace std;
int n,m,k,x[N],y[N],z,d[N],root[8*N],w,h,flag[N*3],fl[N];
struct ques{
int t,a,b,c,d,id;
bool operator <(const ques &s)const{return t>s.t;}
}tmp,now;
priority_queue<ques> q;
vector<ques> g[N];
struct fhq{
int l[N*20],r[N*20],val[N*20],id[N*20],key[N*20],cnt,root1,root2,root3;
I int newnode(int x,int ids){id[++cnt]=ids;val[cnt]=x;key[cnt]=rand();return cnt;}
I void split(int x,int now,int &a,int &b){
if(!now)return (void)(a=b=0);
val[now]<=x?(a=now,split(x,r[now],r[now],b)):(b=now,split(x,l[now],a,l[now]));
}
I int merge(int x,int y){
if(!x||!y) return x|y;
return key[x]<key[y]?(r[x]=merge(r[x],y),x):(l[y]=merge(x,l[y]),y);
}
I void insert(int x,int &root,int id){split(x,root,root1,root2);root=merge(root1,merge(newnode(x,id),root2));}
I void del(int x,int y,int z,int &root){split(x-1,root,root1,root2);split(y,root2,root2,root3);dfs(root2,z);root=merge(root1,root3);}
I void dfs(int x,int w){
if(!x) return;int i;dfs(l[x],w);dfs(r[x],w);
if(!fl[id[x]]){d[id[x]]=w,fl[id[x]]=1;for(i=0;i<g[id[x]].size();i++) tmp=g[id[x]][i],!flag[tmp.id]&&(tmp.t+=w,q.push(tmp),flag[tmp.id]=1);}
}
}s;
I void get(int x,int y,int id,int l=1,int r=w,int now=1){
s.insert(y,root[now],id);if(l==r)return ;
int m=l+r>>1;x<=m?(get(x,y,id,l,m,l(now))):(get(x,y,id,m+1,r,r(now)));
}
I void del(int x,int y,int a,int b,int z,int l=1,int r=w,int now=1){
if(x<=l&&r<=y){s.del(a,b,z,root[now]);return ;}int m=l+r>>1;
(x<=m)&&(del(x,y,a,b,z,l,m,l(now)),0);(y>m)&&(del(x,y,a,b,z,m+1,r,r(now)),0);
}
I void read(int &x){
char s=getchar();x=0;
while(s<'0'||s>'9') s=getchar();
while(s>='0'&&s<='9') x=x*10+s-48,s=getchar();
}
int main(){
freopen("1.in","r",stdin);//freopen("1.out","w",stdout);
srand(51569);register int i,j;scanf("%d%d%d%d",&n,&m,&w,&h);memset(d,0x7f,sizeof(d));
for(i=1;i<=n;i++) read(x[i]),read(y[i]),get(x[i],y[i],i);q.push((ques){0,x[1],x[1],y[1],y[1]});
for(i=1;i<=m;i++) read(z),read(tmp.t),read(tmp.a),read(tmp.b),read(tmp.c),read(tmp.d),tmp.id=i,g[z].push_back(tmp);//return 0;
for(i=1;i<=m+1;i++)now=q.top(),q.pop(),del(now.a,now.b,now.c,now.d,now.t);
for(i=2;i<=n;i++) printf("%d\n",d[i]);
}