嘘~ 正在从服务器偷取页面 . . .


洛谷P1963 [NOI2009] 变换序列 题解

题目链接:P1963 [NOI2009] 变换序列

题意

对于 $N$ 个整数 $0, 1, \cdots, N-1$ ,一个变换序列 $T$ 为这 $N$ 个整数的一个置换。

定义 $x$ 和 $y$ 之间的距离($x,y = 0,1,\cdots,n-1$)

给定每个 $i$ 和 $T_i$ 之间的距离 $D(i,T_i)$

你需要求出一个满足要求的且字典序最小的变换序列 $T$ 。

说明:对于两个变换序列 $S$ 和 $T$ ,如果存在 $p<N$ ,满足对于$i=0,1,\cdots,p-1$, $S_i=T_i$ 且 $S_p<T_p$ ,我们称 $S$ 比 $T$ 字典序小。

原题真的废话连篇还特别丑,已经删减过了。

输入格式

第一行包含一个整数 $N$ ,表示序列的长度。接下来的一行包含 $N$ 个整数 $D_i$ ,其中 $D_i$ 表示 $i$ 和 $T_i$ 之间的距离。

输出格式

如果至少存在一个满足要求的变换序列 $T$ ,则输出文件中包含一行 $N$ 个整数,表示你计算得到的字典序最小的 $T$ ;否则输出No Answer(不含引号)。注意:输出文件中相邻两个数之间用一个空格分开,行末不包含多余空格。

数据范围

对于 $100\%$ 的数据,满足:$N\le 10^4$。

这道题很好的考察了对匈牙利算法的理解。

注意到这是一个置换,即满足双射的关系,考虑建立二分图。

对于每个左侧 $x$ ,我们可以将它与右侧 $x+D_x,~x-D_x,~x-n+D_x,~x+n-D_x$ 连边

实际上,$x+D_x \le n$ 时,$x-n+D_x \le 0$ ,因此这四条边里面只会有两条存在

不难发现他们就是 $(x+D_x) \bmod n,~(x+n-D_x) \bmod n$

因此如果这张图存在二分图完美匹配,就一定有解。

但是题目要求的是字典序最小的解,这个有些麻烦。

考虑匈牙利算法的过程。

对于顺序枚举的每个点 $u$ ,它会遍历它所有的边 $(u,v_i)$ (按照存储顺序)并尝试匹配。

  • 如果 $v_i$ 没有被匹配过,那么它会被直接匹配。

  • 如果 $v_i$ 存在匹配的左部点,那就递归地“强行”让之前的点换匹配。

    直到 $v_i$ 可以被匹配,或者发现之前的点改不了匹配(也就是 $v_i$ 无论如何也匹配不了)

不难发现匈牙利算法会使每个后匹配的点 $u$ ,优先匹配它最前的一条边 $(u,v_i)$ 的 $v_i$ 。

如果把 $u$ 的每条边 $(u,v)$ ,按 $v$ 的大小顺序排序后存储

那么对于每个后匹配的点 $u$ ,它会优先匹配最小的一个 $v_i$ 。

注意到字典序是第一个值优先,而匈牙利算法是最后一个匹配的优先

因此我们倒着匹配就可以得到字典序最小的解

时间复杂度 $O(nm)$

代码:

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
void up(int &x,int y) { x < y ? x = y : 0; }
void down(int &x,int y) { x > y ? x = y : 0; }
#define N ((int)(1e5+15))

int n,to[N][2],p[N],g[N],used[N];
bool dfs(int u,int tim)
{
    for(int t=0; t<2; t++)
    {
        int v=to[u][t];
        if(used[v] != tim) // !used[v];
        {
            used[v] = tim; // used[v]=1;
            if(p[v] == -1 || dfs(p[v],tim))
                { p[v] = u; g[u] = v; return 1; }
        }
    }
    return 0;
}
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    // freopen("check.in","r",stdin);
    // freopen("check.out","w",stdout);
    cin >> n;
    for(int i=0,x; i<n; i++)
    {
        cin >> x;
        to[i][0] = (i + x) % n;
        to[i][1] = (i - x + n) % n;
        if(to[i][0] > to[i][1]) swap(to[i][0], to[i][1]); // v1 < v2
    }
    memset(p,-1,sizeof(p));
    int tim=0,i;
    for(i=n-1; i>=0; i--) if(!dfs(i,++tim)) break;
    if(~i) cout << "No Answer\n";
    else for(int i=0; i<n; i++) cout << g[i] << " \n"[i==n-1];
    return 0;
}


文章作者: q779
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-ND 4.0 许可协议。转载请注明来源 q779 !
评论
  目录