题目

Byteland一直以奇妙的跳舞蝇而闻名于世。驯养的苍蝇能和着音乐的节奏精确地做每一次飞跃。通常,训练者会在
桌上放一排硬币,这些硬币的排列并不按照特定的顺序。每枚硬币上都有一行题字:i→j,i是这枚硬币的编号,j
是站在硬币i上的苍蝇下一步应该飞往的硬币编号。训练者在每个硬币上放一只苍蝇,然后开始放音乐。那些苍蝇
就跟着音乐的节拍开始跳舞,在每一拍中苍蝇都会直接跳到编号为j的硬币上。在舞蹈中,可能会出现多只苍蝇在
同一硬币上的情况。这样,跳舞蝇就会一起继续表演。假定有n只苍蝇,n枚硬币。则一旦确定了n枚硬币上的题字
,那么这场表演也就确定了。然而,对硬币不同的设置也可能导致相同的表演,只要我们把硬币按适当的顺序排列
让我们先来看3枚硬币上的表演。
如果表演是这样进行:
从第一个硬币到第二个,第二个到第三个,第三个又回到第一个硬币(表示为1→2,2→3,3→1)。
具体表演是3只苍蝇绕圈跳跃,从不相遇。那么表演1→2,2→3,3→3,则是与其不同的。
因为该表演为:两拍后,所有的苍蝇在第3枚硬币上相遇,然后一直在原地跳跃。
但是,设置1→2,2→3,3→2,4→4和1→1、2→3、3→2、4→3就是同样的类型。
如果你把前者的硬币从左到右排列,而把后者从右到左排列,就会看到相同的表演。
任务:
如果跳舞蝇的两次表演是相同的,就会使观众不满,请编写一个程序:
读入测试任务的个数;
对每一个测试任务,从PCH.IN中读入两组硬币设置,验证是否能把硬币按一定顺序排列,使跳舞蝇给出相同的表演;

思路

建立模型很重要。

首先这是一个有向图组成的森林。

由于每条边的出度都为1,所以这实际上是由基环树组成的森林。题目的意思就是要比较两个基环树森林是否一致。

对于这种问题,一般都借助于hash来解决。

下面将问题拆分:

  1. 一棵树的hash可以借助以下代码实现:每个点向上跳的时候将其携带的信息放到父亲节点上。
int get_hash(int x,int f){
    int res=1;
    for(int i=h[x];i;i=G[i].nxt){
        int u=G[i].to;
        if(u==f||vis[u])continue;
        res=(res+1LL*get_hash(u,x)*base%mod)%mod;
    }
    return res;
}
  1. 一个基环树将环拆了之后,可以知道每个树的hash值,然后爆扫断环的起点,将取最终得到的树的hash值最小的那个。
  2. 将所有基环树的hash值排序,从小到大再进行一次hash

这样就实现了,相同的森林得到的hash值相同的效果。

ps:此题数据巨水

代码

#include<bits/stdc++.h>
#define M 10005
#define clr(x,y) memset(x,y,sizeof(x))
using namespace std;
const int base=233;
const int mod=1e9+7;
int d,n;
int h[M],tt;
struct edge{
    int nxt,to;
}G[M<<1];
void Add(int a,int b){
    G[++tt]=(edge){h[a],b};
    h[a]=tt;
}
int stk[M],top;
bool vis[M],mark[M];
int A[M],Pow[M];
bool dfs_lop(int x,int f){
    if(vis[x])return 1;
    vis[x]=mark[x]=1;
    stk[++top]=x;
    for(int i=h[x];i;i=G[i].nxt){
        int u=G[i].to;
        if(u==f)continue;
        if(dfs_lop(u,x))return 1;
    }
    top--;vis[x]=0;
    return 0;
}
int get_hash(int x,int f){
    int res=1;
    for(int i=h[x];i;i=G[i].nxt){
        int u=G[i].to;
        if(u==f||vis[u])continue;
        res=(res+1LL*get_hash(u,x)*base%mod)%mod;
    }
    return res;
}
int QQ[M],Qcnt;
int hav[M],hcnt;
int get_has(){
    Qcnt=0;clr(vis,0);clr(mark,0);
    hcnt=0;
    for(int i=1;i<=n;i++){
        if(!mark[i]){
            top=0;
            if(dfs_lop(i,0))hav[++hcnt]=i;
        }
    }
    clr(mark,0);
    for(int j=1;j<=hcnt;j++){
        top=0;
        dfs_lop(hav[j],0);
        for(int i=1;i<=top;i++)A[i]=get_hash(stk[i],0);
        int mi=2e9,res=0;
        for(int i=1;i<=top;i++)res=(1LL*res*base+A[i])%mod;mi=res;
        for(int i=2;i<=top;i++){
            res=((res-1LL*A[i-1]*Pow[top-1]%mod)%mod+mod)%mod;
            res=(1LL*res*base+A[i-1])%mod;
            mi=min(res,mi);
        }
        QQ[++Qcnt]=mi;
    }
    for(int i=1;i<=n;i++){
        if(!mark[i]){
            QQ[++Qcnt]=get_hash(i,0);
        }
    }
    sort(QQ+1,QQ+Qcnt+1);
    int res=0;
    for(int i=1;i<=Qcnt;i++)
        res=(1LL*res*base%mod+QQ[i])%mod;
    return res;
}
int main(){
    Pow[0]=1;
    for(int i=1;i<=2000;i++)Pow[i]=1LL*base*Pow[i-1]%mod;
    scanf("%d",&d);
    while(d--){
        scanf("%d",&n);
        clr(h,0);tt=0;
        for(int i=1,x;i<=n;i++)scanf("%d",&x),Add(x,i);
        int a=get_has();
        clr(h,0);tt=0;
        for(int i=1,x;i<=n;i++)scanf("%d",&x),Add(x,i);
        int b=get_has();
        if(a==b)puts("T");
        else puts("N");
    }
    return 0;
}