妙啊。。。。。O(N)算法。
首先每个<n的点只连两条边。那么这就是一个环套树啊。。。。。
然后找到在环上编号最小的点,向最小的方向更新答案。一个联通块确定了一对,就确定了这个联通块所有的样子。
所以两遍dfs就好了。23333333orz blutrex。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define maxn 10050 #define maxv 20050 #define maxe 100500 using namespace std; int n,d[maxn],g[maxv],nume=1,deg[maxv],ret=0,ans[maxn],dis[maxv],pre[maxv]; int stack1[maxv],stack2[maxv],top=0; struct edge { int v,nxt; }e[maxe]; bool vis[maxv],lab[maxv]; void addedge(int u,int v) { e[++nume].v=v; e[nume].nxt=g[u]; g[u]=nume; } void dfs1(int x,int fath) { vis[x]=true; for (int i=g[x];i;i=e[i].nxt) { int v=e[i].v; if (vis[v]) { if (v==fath) { if (i!=(pre[x]^1)) stack1[++top]=x,stack2[top]=v; else continue; } else if (dis[v]<dis[x]) stack1[++top]=x,stack2[top]=v; } else {dis[v]=dis[x]+1;pre[v]=i;dfs1(v,x);} } } void dfs2(int x,int type) { vis[x]=true; for (int i=g[x];i;i=e[i].nxt) { int v=e[i].v; if (vis[v]) continue; if (!type) {ans[x]=v-n;dfs2(v,type^1);} else dfs2(v,type^1); } } int main() { scanf("%d",&n); for (int i=0;i<n;i++) ans[i]=-1; for (int i=0;i<n;i++) { scanf("%d",&d[i]); addedge(i,(i+d[i])%n+n);addedge((i+d[i])%n+n,i); addedge(i,(i-d[i]+n)%n+n);addedge((i-d[i]+n)%n+n,i); deg[i]+=2;deg[(i+d[i])%n+n]++;deg[(i-d[i]+n)%n+n]++; } for (int i=0;i<2*n;i++) if (!vis[i]) dfs1(i,-1); for (int i=1;i<=top;i++) { int now=stack1[i]; while (now!=stack2[i]) { lab[now]=true; now=e[pre[now]^1].v; } lab[stack2[i]]=true; } memset(vis,false,sizeof(vis)); for (int i=0;i<n;i++) { if (!lab[i]) continue; if (vis[i]) continue; int mn=3*n; for (int j=g[i];j;j=e[j].nxt) mn=min(mn,e[j].v); ans[i]=mn-n;vis[i]=true;dfs2(mn,1); } for (int i=0;i<n;i++) if (ans[i]==-1) {printf("No Answer");return 0;} for (int i=0;i<n-1;i++) printf("%d ",ans[i]);printf("%d",ans[n-1]); return 0; }