题目绕了半天,其实很简单……
对于任意
i∈[0,n)
,有
Di=min{|i−Ti|,n−|i−Ti|}
已知Di的情况下,求Ti
其实讨论一发就会发现,有4种可能:
Ti=i+Di或i+Di−n或i−Di或i−Di+n
其实有两对互相矛盾的存在,那么每个i就一定只有2种可能了
其实i与Ti构成了二分图。
建图后刷出最大匹配即可。
然而……题目要求字典序最小
于是邻接表把Ti大的边先建,然后从后往前增广i
这样就能保证字典序最小,因为匈牙利算法是对后处理的节点优先
示例程序:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=20005,maxe=20005;
int n,res;
int tot,son[maxe],nxt[maxe],lnk[maxn];
inline void add(int x,int y){
son[++tot]=y;nxt[tot]=lnk[x];lnk[x]=tot;
}
int con[maxn],vis[maxn],Tim=0,ans[maxn];
bool find(int x){
for (int j=lnk[x];j;j=nxt[j])
if (vis[son[j]]!=Tim){
vis[son[j]]=Tim;
if (con[son[j]]==-1||find(con[son[j]])) return con[son[j]]=x,1;
}
return 0;
}
int main(){
scanf("%d",&n);
for (int i=0;i<n;i++){
int d;scanf("%d",&d);
int a=(i+d)%n+n,b=(i-d+n)%n+n;
if (a<b) swap(a,b);
add(i,a);add(i,b);
}
memset(con,255,sizeof(con));
for (int i=n-1;i>=0;i--)
Tim++,res+=find(i);
for (int i=n;i<2*n;i++) ans[con[i]]=i-n;
if (res!=n) return printf("No Answer"),0;
printf("%d",ans[0]);
for (int i=1;i<n;i++) printf(" %d",ans[i]);
return 0;
}