题目大意:
有一个n的排列P ,定义D(x, y) = min(|x − y|, n − |x − y|),给
出D(i, P i ),求出满足条件的字典序最小的P ,或者判断无解。
n ≤ 10 4 .
思路:
显然是是一个二分图匹配,但是题目要求满足字典序最小,考虑每一个点加边从小到达加,先加终点的编号小的边。
按照上述方式跑匈牙利,如果点从编号小的往大的跑增广路,会出现后面的点把前面的编号小的匹配给占用掉而导致答案不更优,所以反过来跑匈牙利即可。
匈牙利是
nm
n
m
的,这一题因为常数较小所以可以跑过去,其实有环套树的线性做法。
每一个下标显然有且仅有两个数字满足条件,设这两个数字为u,v,则在图中连接u,v,则转化为了每一条边只可以选择一个顶点,每个顶点只可以被一条边选择,从而发现题目有解必须每一个连通块都是一个环套树,直接在环套树上贪心选取即可。
匈牙利做法
#include<bits/stdc++.h>
#define REP(i,a,b) for(int i=a,i##_end_=b;i<=i##_end_;++i)
#define DREP(i,a,b) for(int i=a,i##_end_=b;i>=i##_end_;--i)
typedef long long ll;
using namespace std;
void File(){
freopen("bzoj1562.in","r",stdin);
freopen("bzoj1562.out","w",stdout);
}
template<typename T>void read(T &_){
T __=0,mul=1; char ch=getchar();
while(!isdigit(ch)){
if(ch=='-')mul=-1;
ch=getchar();
}
while(isdigit(ch))__=(__<<1)+(__<<3)+(ch^'0'),ch=getchar();
_=__*mul;
}
const int maxn=10000+10;
int n,d[maxn],to[maxn][2],be[maxn],ans[maxn],cnt;
bool vis[maxn];
bool Hungary(int u){
REP(i,0,1){
int v=to[u][i];
if(vis[v])continue;
vis[v]=1;
if(!be[v] || Hungary(be[v])){
be[v]=u;
return true;
}
}
return false;
}
void init(){
read(n);
REP(i,1,n)read(d[i]);
REP(i,1,n){
int num1=i-d[i],num2=i+d[i];
if(num1<=0)num1+=n; if(num1>n)num1-=n;
if(num2<=0)num2+=n; if(num2>n)num2-=n;
if(num1>num2)swap(num1,num2);
to[i][0]=num1; to[i][1]=num2;
}
}
void work(){
DREP(i,n,1){
memset(vis,0,sizeof(vis));
cnt+=Hungary(i);
}
if(cnt!=n){puts("No Answer");return;}
REP(i,1,n)ans[be[i]]=i;
REP(i,1,n)printf("%d ",ans[i]-1);
}
int main(){
// File();
init();
work();
return 0;
}
环套树做法
#include<bits/stdc++.h>
#define REP(i,a,b) for(int i=a,i##_end_=b;i<=i##_end_;++i)
#define DREP(i,a,b) for(int i=a,i##_end_=b;i>=i##_end_;--i)
typedef long long ll;
using namespace std;
void File(){
freopen("bzoj1562.in","r",stdin);
freopen("bzoj1562.out","w",stdout);
}
template<typename T>void read(T &_){
T __=0,mul=1; char ch=getchar();
while(!isdigit(ch)){
if(ch=='-')mul=-1;
ch=getchar();
}
while(isdigit(ch))__=(__<<1)+(__<<3)+(ch^'0'),ch=getchar();
_=__*mul;
}
const int maxn=10000+10;
const int inf=0x3f3f3f3f;
int n,d[maxn],be[maxn];
int beg[maxn],las[maxn<<1],to[maxn<<1],w[maxn<<1],cnte=1,ans[maxn];
void add(int u,int v,int num){
las[++cnte]=beg[u]; beg[u]=cnte; to[cnte]=v; w[cnte]=num;
las[++cnte]=beg[v]; beg[v]=cnte; to[cnte]=u; w[cnte]=num;
}
void init(){
read(n);
REP(i,1,n)read(d[i]);
REP(i,1,n){
int num1=i-d[i],num2=i+d[i];
if(num1<=0)num1+=n; if(num1>n)num1-=n;
if(num2<=0)num2+=n; if(num2>n)num2-=n;
add(num1,num2,i);
}
}
stack<int>stk;
vector<int>c[maxn];
bool vis[maxn],in[maxn];
int flag;
void dfs(int u,int fr){
if(vis[u]){
if(flag)return;
flag=u;
for(int p=0;to[p^1]!=u;stk.pop()){
p=stk.top();
c[u].push_back(p);
in[to[p]]=1;
}
return;
}
vis[u]=1;
for(int i=beg[u];i;i=las[i]){
if(i==(fr^1))continue;
stk.push(i);
dfs(to[i],i);
if(!flag)stk.pop();
}
}
void dfs_tree(int u,int f){
for(int i=beg[u];i;i=las[i]){
if(to[i]==f || in[to[i]])continue;
ans[w[i]]=to[i];
dfs_tree(to[i],u);
}
}
void dfs_circle(int u,int fr,int rt){
for(int i=beg[u];i;i=las[i]){
if(!in[to[i]])continue;
if(i==(fr^1) || to[i]==rt)continue;
ans[w[i]]=to[i];
dfs_circle(to[i],i,rt);
}
}
void work(){
REP(i,1,n)if(!vis[i]){
flag=0;
while(!stk.empty())stk.pop();
dfs(i,0);
if(!flag){puts("No Answer");return;}
int Min=inf,num=0;
for(int sz=c[flag].size()-1,j=0;j<=sz;++j){
dfs_tree(to[c[flag][j]],0);
if(w[c[flag][j]]<Min)Min=w[c[flag][j]],num=c[flag][j];
}
if(to[num]<to[num^1]){
ans[w[num]]=to[num];
dfs_circle(to[num],num,to[num]);
}
else{
ans[w[num]]=to[num^1];
dfs_circle(to[num^1],num^1,to[num^1]);
}
}
REP(i,1,n)printf("%d ",ans[i]-1);
}
int main(){
File();
init();
work();
return 0;
}