【二分图最大匹配】BZOJ1562 [NOI2009]变换序列

来源:互联网 发布:摇色子软件 编辑:程序博客网 时间:2024/05/17 04:58

题面在这里

题目绕了半天,其实很简单……

对于任意 i[0,n),有Di=min{|iTi|,n|iTi|}
已知Di的情况下,求Ti
其实讨论一发就会发现,有4种可能:
Ti=i+Dii+DiniDiiDi+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;} 
原创粉丝点击