约会 Rendezvous
内存限制:128 MiB 时间限制:1000 ms 标准输入输出
题目描述
Q:这题是不是非常简单?
A:毒瘤题。
Q:毒瘤出题人?
A:毒瘤出题人。
Q:是不是比较考验码力,打完想对就能A?
A:毒瘤卡常,你没有一点卡常技巧是过不了的。
Q:我卡卡常就能A了是吗?
A:卡dfs 卡你空间,卡你时间,还特别容易爆栈 你需要特别的姿势!
34个测试点,让你绝望
没有看题解过了的毒瘤题
对于我这个蒟蒻,我调了一天,整整一天,
day1 晚上开始码 没有看题解 大约想了想,好像可以建反边跑lca
然后思考它有什么性质,首先题目里保证了只有一个出边,那么相当与保证了每个图都有一个环
想到可以缩点然后无脑lca 然后又想了想 好像环上比较难处理
day1 晚上码完 得了 38分 稍微改了改 28分
day2 重新理了理思路,想到环上可以预处理,但没想到怎么处理,随手打了个单调队列发现不行
得到了3分的好成绩
#include<bits/stdc++.h> #define ll long long #define A 600000 using namespace std; ll head[A],nxt[A],belong[A],ver[A],tot=0,deep[A],dis[A],t,n,m,dfn[A],sta[A],otp=0,num=0,top=0,low[A],f[A][30],cnt=0,ins[A],sum[A],rt[A],bl[A],zuzong[A],fa[A],bl4[A]; vector <ll> scc[251001]; bool flag[A]; void add(ll x,ll y) {nxt[++tot]=head[x];ver[tot]=y;head[x]=tot;} inline ll lca(ll x,ll y) { if(deep[x]>deep[y])swap(x,y); for(ll i=t;i>=0;i--) { if(deep[x]==deep[y]) break; if(deep[x]<=deep[f[y][i]]) y=f[y][i]; } if(x==y) return x; for(ll i=t;i>=0;i--) if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i]; return f[x][0]; } void dfs(ll x,ll st,ll t) { deep[x]=st,flag[x]=1;bl4[x]=otp; for(ll i=head[x];i;i=nxt[i]) { ll y=ver[i]; if(flag[y]) continue; rt[y]=t; dis[y]=dis[x]+1; f[y][0]=x; dfs(y,st+1,t); } return ; } ll read() { ll f=1,x=0;char c=getchar(); while(!isdigit(c)){if(c=='-') f=-1;c=getchar();} while(isdigit(c)){x=(x<<1)+(x<<3)+c-'0';c=getchar();} return f*x; } inline void tarjan(ll x) { dfn[x]=low[x]=++num; sta[++top]=x;ins[x]=1; for(ll i=head[x];i;i=nxt[i]) { ll y=ver[i]; if(dfn[y]==0) { tarjan(y); low[x]=min(low[x],low[y]); } else if(ins[y]) low[x]=min(low[x],dfn[y]); } if(dfn[x]==low[x]) { ++cnt; ll yy=0,cis=0,lu; while(1) { yy=sta[top--]; ins[yy]=0; cis++; bl4[yy]=cnt; if(yy==x) break; scc[cnt].push_back(yy); belong[yy]=-1; //printf("将%lld赋成-1 ",yy); } if(cis>1) scc[cnt].push_back(x),belong[x]=-1; else cnt--; } } void tiaotarjan() { cout<<"***"<<endl; for(ll i=1;i<=cnt;i++) { for(ll j=0;j<scc[i].size();j++) { cout<<scc[i][j]<<" "; } if(scc[i].size()) cout<<endl; } cout<<"***"<<endl; } void tiaos() { } void work() { n=read();m=read(); t=log(n)/log(2)+2; for(ll i=1;i<=n;i++) { ll xx=read(); add(xx,i); //建反图跑tarjan缩点+lca } for(ll i=1;i<=n;i++) if(!dfn[i]) tarjan(i); top=0; memset(flag,0,sizeof(flag)); for(ll i=1;i<=cnt;i++) { ll size=scc[i].size();otp++; for(ll ii=0;ii<size;ii++) { ll q=scc[i][ii]; for(ll j=head[q];j;j=nxt[j]) { ll y=ver[j]; if(belong[y]!=-1) { rt[y]=q; dfs(y,1,rt[y]); f[y][0]=y; } } } } for(ll j=1;j<=t;j++) for(ll i=1;i<=n;i++) f[i][j]=f[f[i][j-1]][j-1]; /*for(ll i=1;i<=n;i++) { if(belong[i]!=-1) { printf("i=%lld rt=%lld deep=%lld ",i,rt[i],deep[i]); } }*/ for(ll i=1;i<=m;i++) { ll xx=read(),yy=read(); if(bl4[xx]==bl4[yy]) { ll lc=lca(xx,yy); //printf("rtx=%lld rty=%lld ",rt[xx],rt[yy]); if(rt[xx]==rt[yy]) { printf("%lld %lld ",dis[xx]-dis[lc],dis[yy]-dis[lc]); } if(rt[xx]!=rt[yy]) { ll de1=deep[xx],de2=deep[yy]; ll now=bl4[rt[xx]]; ll size=scc[now].size(),ott=0; bool kais=0; for(ll ii=0;ii<size*2-1;ii++) { if(ii>size) { ll j=ii-size; if(!kais&&(scc[now][ii]==rt[xx]||scc[now][ii]==rt[yy])) { kais=1; } else if(kais==1) { de1<de2?de1++:de2++; if(scc[now][ii]==rt[xx]||scc[now][ii]==rt[yy]) { kais=0; break; } } } else { if(!kais&&(scc[now][ii]==rt[xx]||scc[now][ii]==rt[yy])) { kais=1; } else if(kais==1) { de1<de2?de1++:de2++; if(scc[now][ii]==rt[xx]||scc[now][ii]==rt[yy]) { kais=0; break; } } } } printf("%lld %lld ",de1,de2); } } else printf("-1 -1 "); } } int main() {work();}
和同学讨论这个题发现他们也挺艰难的
day2 下午 然后经过艰难的辨认+艰难的手膜得到以下代码
ll lx,ly,bl=belong[ances[x]],rootx=ances[x],rooty=ances[y],disx=deep[x]-deep[rootx],disy=deep[y]-deep[rooty],xy,yx; for(ll j=0;j<scc[bl].size();j++) { if(scc[bl][j]==rootx) lx=j; if(scc[bl][j]==rooty) ly=j; } if(lx<ly) xy=ly-lx,yx=sz[bl]-(ly-lx); else yx=lx-ly,xy=sz[bl]-(lx-ly); if(max(disx+xy,disy)<max(disx,disy+yx)) printf("%lld %lld ",disx+xy,disy); else if(max(disx+xy,disy)>max(disx,disy+yx)) printf("%lld %lld ",disx,disy+yx); else { if(min(disx+xy,disy)<min(disx,disy+yx)) printf("%lld %lld ",disx+xy,disy); else if(min(disx+xy,disy)>min(disx,disy+yx)) printf("%lld %lld ",disx,disy+yx); else if(disx+xy>=disy) printf("%lld %lld ",disx+xy,disy); else printf("%lld %lld ",disx,disy+yx); }
然后TLE了
得知tarjan一定会被卡死
然后改成了dfs(??????)
终于吧MLE整过了之后,就接着TLE
经过几个小时卡常斗争终于A了
#include<bits/stdc++.h> #define ll int #define A 510000 const int L=1<<20|1; char buffer[L],*S,*T; #define getchar() ((S==T&&(T=(S=buffer)+fread(buffer,1,L,stdin),S==T))?EOF:*S++) using namespace std; ll head[A],nxt[A],belong[A],ver[A],tot=0,deep[A],t,n,m,otp=0,num=0,f[A][22],cnt=0,bl[A],ances[A],last,sz[A],v[A],cixu[A],chushi=0; inline ll find(ll x) {if(ances[x]==x) return x;return ances[x]=find(ances[x]);} inline void add(ll x,ll y) {nxt[++tot]=head[x];ver[tot]=y;head[x]=tot;} inline ll lca(ll x,ll y) { if(deep[x]>deep[y])swap(x,y); ll w; for(w=0;(1<<w)<=deep[y];w++); w--; for(ll i=w;i>=0;i--) { if(deep[x]==deep[y]) break; if(deep[x]<=deep[f[y][i]]) y=f[y][i]; } if(x==y) return x; for(ll i=t;i>=0;i--) if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i]; return f[x][0]; } inline void dfs(ll x,ll pre) { v[x]=++num; deep[x]=deep[pre]+1; // printf("%lld ",deep[x]); for(ll i=1;(1<<i)<=deep[x];i++) f[x][i]=f[f[x][i-1]][i-1]; for(ll i=head[x];i;i=nxt[i]) { ll y=ver[i]; if(v[y]>last) { cnt++; chushi=0; cixu[x]=++chushi; sz[cnt]++; belong[x]=cnt; ances[x]=x; for(ll i=x;i!=y;i=f[i][0]) { cixu[f[i][0]]=++chushi; belong[f[i][0]]=cnt; ances[f[i][0]]=f[i][0]; sz[cnt]++; } } else { ances[y]=f[y][0]=x ,dfs(y,x); } } } inline ll Read(){ register ll ret; register char r; while(r=getchar(),r<'0'||r>'9');ret=r-48; while(r=getchar(),r>='0'&&r<='9')ret=ret*10+r-48; return ret; } inline void work() { last=0; n=Read();m=Read(); t=log(n)/log(2)+1; for(ll i=1;i<=n;i++) { ll xx=Read(); add(xx,i); } for(ll i=1;i<=n;i++) if(!v[i]) { ances[i]=f[i][0]=i; dfs(i,0); last=num; } for(ll i=1;i<=m;i++) { ll x=Read(),y=Read(); if(find(x)!=find(y)&&belong[ances[x]]!=belong[ances[y]]) printf("-1 -1 "); else { if(ances[x]==ances[y]) { ll lc=lca(x,y); printf("%d %d ",deep[x]-deep[lc],deep[y]-deep[lc]); } else { ll lx,ly,bl=belong[ances[x]],disx=deep[x]-deep[ances[x]],disy=deep[y]-deep[ances[y]],xy,yx; lx=cixu[ances[x]];ly=cixu[ances[y]]; if(lx<ly) xy=ly-lx,yx=sz[bl]-(ly-lx); else yx=lx-ly,xy=sz[bl]-(lx-ly); if(max(disx+xy,disy)<max(disx,disy+yx)) printf("%d %d ",disx+xy,disy); else if(max(disx+xy,disy)>max(disx,disy+yx)) printf("%d %d ",disx,disy+yx); else { if(min(disx+xy,disy)<min(disx,disy+yx)) printf("%d %d ",disx+xy,disy); else if(min(disx+xy,disy)>min(disx,disy+yx)) printf("%d %d ",disx,disy+yx); else if(disx+xy>=disy) printf("%d %d ",disx+xy,disy); else printf("%d %d ",disx,disy+yx); } } } } } main() {work();}
具体思路
首先我们会发现图中一定存在环而且仅仅存在一个环,可能有多个可分割的图,每个图都有一个环
我们要建反图,这样我们就可以跑lca了
然后我们模拟就完了,对于同一个图上的有如下情况
一.路径不经过环
lca完了
二.路径经过环
我们发现我们缩点时其实是按照一定顺序缩的,事实上是按照逆边顺序缩的
于是我们维护一个类似于dfn序的东西就完了
只在环上维护dfn序,相减就得到了距离
对于不在一个图上的直接-1 -1
#include<bits/stdc++.h> #define ll int #define A 510000 const int L=1<<20|1; char buffer[L],*S,*T; #define getchar() ((S==T&&(T=(S=buffer)+fread(buffer,1,L,stdin),S==T))?EOF:*S++) using namespace std; ll head[A],nxt[A],belong[A],ver[A],tot=0,deep[A],t,n,m,otp=0,num=0,f[A][22],cnt=0,bl[A],ances[A],last,sz[A],v[A],cixu[A],chushi=0; inline ll find(ll x) {if(ances[x]==x) return x;return ances[x]=find(ances[x]);} inline void add(ll x,ll y) {nxt[++tot]=head[x];ver[tot]=y;head[x]=tot;} inline ll lca(ll x,ll y) { if(deep[x]>deep[y])swap(x,y); ll w; for(w=0;(1<<w)<=deep[y];w++); w--; for(ll i=w;i>=0;i--) { if(deep[x]==deep[y]) break; if(deep[x]<=deep[f[y][i]]) y=f[y][i]; } if(x==y) return x; for(ll i=t;i>=0;i--) if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i]; return f[x][0]; } inline void dfs(ll x,ll pre) { v[x]=++num; deep[x]=deep[pre]+1; // printf("%lld ",deep[x]); for(ll i=1;(1<<i)<=deep[x];i++) f[x][i]=f[f[x][i-1]][i-1]; for(ll i=head[x];i;i=nxt[i]) { ll y=ver[i]; if(v[y]>last) { cnt++; chushi=0; cixu[x]=++chushi; sz[cnt]++; belong[x]=cnt; ances[x]=x; for(ll i=x;i!=y;i=f[i][0]) { cixu[f[i][0]]=++chushi; belong[f[i][0]]=cnt; ances[f[i][0]]=f[i][0]; sz[cnt]++; } } else { ances[y]=f[y][0]=x ,dfs(y,x); } } } inline ll Read(){ register ll ret; register char r; while(r=getchar(),r<'0'||r>'9');ret=r-48; while(r=getchar(),r>='0'&&r<='9')ret=ret*10+r-48; return ret; } inline void work() { last=0; n=Read();m=Read(); t=log(n)/log(2)+1; for(ll i=1;i<=n;i++) { ll xx=Read(); add(xx,i); } for(ll i=1;i<=n;i++) if(!v[i]) { ances[i]=f[i][0]=i; dfs(i,0); last=num; } for(ll i=1;i<=m;i++) { ll x=Read(),y=Read(); if(find(x)!=find(y)&&belong[ances[x]]!=belong[ances[y]]) printf("-1 -1 "); else { if(ances[x]==ances[y]) { ll lc=lca(x,y); printf("%d %d ",deep[x]-deep[lc],deep[y]-deep[lc]); } else { ll lx,ly,bl=belong[ances[x]],disx=deep[x]-deep[ances[x]],disy=deep[y]-deep[ances[y]],xy,yx; lx=cixu[ances[x]];ly=cixu[ances[y]]; if(lx<ly) xy=ly-lx,yx=sz[bl]-(ly-lx); else yx=lx-ly,xy=sz[bl]-(lx-ly); if(max(disx+xy,disy)<max(disx,disy+yx)) printf("%d %d ",disx+xy,disy); else if(max(disx+xy,disy)>max(disx,disy+yx)) printf("%d %d ",disx,disy+yx); else { if(min(disx+xy,disy)<min(disx,disy+yx)) printf("%d %d ",disx+xy,disy); else if(min(disx+xy,disy)>min(disx,disy+yx)) printf("%d %d ",disx,disy+yx); else if(disx+xy>=disy) printf("%d %d ",disx+xy,disy); else printf("%d %d ",disx,disy+yx); } } } } } main() {work();}