P2582 函数

题目背景

Alice和Bob玩游戏

题目描述

Alice给出一个n的全排列表示一个函数f,即给出的第i个数字即为f(i)。

现在Bob需要给出一个字典序尽可能小的函数g,使得对于任意i,f(g(i))=g(f(i))

输入输出格式

输入格式:

第一行一个整数n

第二行n个整数,依次表示f(1)、f(2)...f(n)

输出格式:

共一行,n个整数,依次表示g(1)、g(2)...g(n)

很绕的一个题...

分析一下发现是会按循环的规律来填的。

如果g[i]=j,那么设g[f[x]]=j那么x是唯一的,因为f是个单射函数。假设我们求得了x,那么又有f[g[x]]=g[f[x]]=j。又因为f是单射函数,所以g[x]又被确定了。这样就有循环了

那么题目又是让我们求字典序最小,所以肯定是从小到大的试填,填了g[i]之后我们又可以继续根据找到上面的规律继续填下去。如果你不按这个规律填肯定就出错了。如果填到最后发现有循环了,也就是这个位置之前被填过了,而且是我们将要填的这个数,就说明这个循环是可行的了。不然的话这个循环就和之前填的是有冲突的了,我们就要试下一个数字了。

解决求x是很简单的,我们只要在一开始读入是时候预处理一下就行了

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<queue>
#include<map>
#include<stack>
#include<string>
#include<algorithm>
#include<cmath>
#define rg register
#define il inline
using namespace std;
typedef unsigned long long ll;
ll read(){
    ll ans=0,flag=1;char ch;
    while((ch=getchar())<'0'||ch>'9') if(ch=='-') flag=-1;
    ans=ch^48;
    while((ch=getchar())>='0'&&ch<='9') ans=(ans<<3)+(ans<<1)+(ch^48);
    return flag*ans;
}
void write(ll x){
    if(x<0){putchar('-');x=-x;}
    if(x>9) write(x/10);
    putchar(x%10+'0');
}
int f[800000+5];
int g[800000+5];
bool cant;//标记是否冲突
void dfs(int now,int k){//now是x,k是要往这里填的数。
    if(g[now]&&g[now]!=k){cant=true;return;}//如果填到这里发现之前被填过了而且不是我们想要填的,就是冲突了。
    if(g[now]==k){cant=false;return;}//这个循环是可行的
    g[now]=k;
    dfs(f[now],f[k]);按找到的规律继续天下去。
    if(cant) g[now]=0;//回溯的时候如果不行就清零
}
int main(){
    int n=read();
    for(rg int i=1;i<=n;i++) f[read()]=i;
    for(rg int i=1;i<=n;i++){//g[1]到g[n]
        if(g[i]) continue;//如果之前填好了就不用填了
        cant=false;
        for(rg int j=1;j<=n;j++){//从1开始试填。
            dfs(i,j);
            if(!cant)break;//填成功了就填下个
        }
    }
    for(rg int i=1;i<n+1;i++){
        write(g[i]);putchar(' ');
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/ffscas/article/details/87862403