DFS+dp 51Nod1448 等幂映射

传送门:点击打开链接

题意:等幂映射是这样定义的,对于一个映射 g : {1,2,...,n} → {1,2,...,n} ,对于所有的 x∈ {1,2,...,n} , g(g(x))=g(x)始终成立。
  f(k)(x) 表示将映射f作用于x上k次的结果。一般的, f(1)(x) = f(x), f(k)(x) = f(f(k−1)(x)) 对于所有的k>1成立。
现在给定一个映射 f : {1,2,...,n} → {1,2,...,n} 。你的任务是寻找最小的k使得 f(k)(x) 是一个等幂映射。
样例解释:这个例子中  f(x) = f(1)(x) 已经是一个等幂映射,因为他已经满足定义: f(f(1))=f(1)=1, f(f(2))=f(2)=2, f(f(3))=f(3)=2,f(f(4))=f(4)=4。

思路:这道题是一道很值得去思考的题,这种映射的题都能转换成图论题!

我们考虑建边i->f(i),那么我们可以发现,等幂映射的图中,所有的环的大小只有1,环外面的点距离最近的环的距离也为1

我们现在再来考虑f(f(x))的图相对于f(x)的图会有什么变化。我们能发现,对于节点i,本来i->f(i),然后f(i)->f(f(i)),之后变成了i->f(f(i))

所以每个点连的边是以前连接的点的下一个。

换句话说,f^k(x)就会相当于在以前的图中,对于i新的边的另一端节点v,将是沿着有向边向下走k步,所到达的点。

对于一个环,最后边是可以连到自己身上来的,那么步数必须是环大小的整数倍。

所以,我们找出所有的环的长度,取lcm。

另外还有一个限制,就是非环上的点,必须要通过k步后能到达环上去。所以取完lcm之后,记为ans,如果非环上点到环上的最近距离最大的那个dist,比lcm要大,

就必须要ans+=lcm,直到ans比dist大才行。

#include <map>
#include <set>
#include <cmath>
#include <ctime>
#include <stack>
#include <queue>
#include <cstdio>
#include <cctype>
#include <bitset>
#include <string>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <functional>
#define fuck(x) cout<<"["<<x<<"]";
#define FIN freopen("input.txt","r",stdin);
#define FOUT freopen("output.txt","w+",stdout);
//#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
const int MX = 2e2 + 5;
const int INF = 0x3f3f3f3f;

int G[MX][MX], n;
int vis[MX], iscir[MX];
int dp[MX];
vector<int>cir;
int DFS(int u, int dep) {
    vis[u] = dep;
    int t, ret = 0;
    
    for(int v = 1; v <= n; v++) {
        if(!G[u][v]) continue;
        if(vis[v] > 0) {
            cir.push_back(dep - vis[v] + 1);
            iscir[u] = 1; ret = vis[v];
        } else if(!vis[v] && (t = DFS(v, dep + 1), t && dep >= t)) {
            iscir[u] = 1; ret = t;
        }
        if(ret) break;
    }
    vis[u] = -1;
    return ret;
}
int DP(int u) {
    if(dp[u]) return dp[u];
    if(iscir[u]) return 0;
    for(int v = 1; v <= n; v++) {
        if(!G[u][v]) continue;
        dp[u] = max(dp[u], DP(v));
    }
    return dp[u] += 1;
}
LL gcd(LL a, LL b) {
    return b ? gcd(b, a % b) : a;
}
LL lcm(LL a, LL b) {
    return a / gcd(a, b) * b;
}
int main() {
    //FIN;
    scanf("%d", &n);
    for(int i = 1; i <= n; i++) {
        int x; scanf("%d", &x);
        G[i][x] = 1;
    }
    for(int i = 1; i <= n; i++) {
        if(!vis[i]) DFS(i, 1);
    }
    
    for(int i = 1; i <= n; i++) vis[i] = 0;
    int dist = 0;
    for(int i = 1; i <= n; i++) {
        dist = max(dist, DP(i));
    }

    int sz = cir.size();
    LL w = 1, ans;
    for(int i = 0; i < sz; i++) {
        w = lcm(w, cir[i]);
    }
    for(ans = w; ans < dist; ans += w);
    printf("%I64d\n", ans);
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值