2017 Multi-University Training Contest

来源:互联网 发布:我的恐怖妻子 知乎 编辑:程序博客网 时间:2024/06/03 14:55

传送门
//这道题真的好, 结合了联通分量和置换群和查分约束的思想, 我还是太菜了啊, 想不到啊.
//题意就是给你一个方程, f(i)=bf(ai) , i 从给所给的序列中依次赋值, 问共有多少中方式使得这个方程成立.
第一分样例可以解释为:
110
111
001
000

思路:实际上就是从a集合到b集合的映射的组合,a中的一个循环节是一个整体,如果b中循环节的长度和a循环节的长度相同或者是因子,那么就可以置换过来,满足这个条件,将结果组合一下就好. 所以关键就是找出每一个环, 且把两个序列中的环都要找出, 并求出环的长度, 也就是找循环节和循环节法因子, 然后把结果乘起来就是了.

//找环不一定用强联通来找, 也可以暴力.
AC Code

/** @Cain*/const int maxn=1e5+5;int cas=1;int bel[maxn],low[maxn],dfn[maxn];vector<int >G[maxn];stack<int > S;int cnt ,res;int cur;int numA[maxn],numB[maxn];int a[maxn],b[maxn];void init(int n){    Fill(low,0);    Fill(dfn,0);    Fill(bel,0);    for(int i=1;i<=n;i++)        G[i].clear();    cnt = 1; res = 0;}void tarjan(int u)   //强联通找环.{    dfn[u] = low[u] = cnt++;    S.push(u);    for(int i=0;i<G[u].size();i++){        int v = G[u][i];        if(!dfn[v]){            tarjan(v);            low[u] = min(low[u],low[v]);        }        else if(!bel[v])            low[u] = min(low[u],dfn[v]);    }    if(low[u] == dfn[u]){        res++;        while(1){            int v = S.top();            S.pop();            bel[v] = res;            if(u == v ) break;        }    }}void solve(){    int n,m;    while(~scanf("%d%d",&n,&m)){        Fill(numA,0); Fill(numB,0);        for(int i=1;i<=n;i++){            scanf("%d",&a[i]);            a[i]++;        }        for(int i=1;i<=m;i++){            scanf("%d",&b[i]);            b[i]++;        }        init(n);        for(int i=1;i<=n;i++){            G[a[i]].push_back(i);        }        for(int i=1;i<=n;i++){            if(!dfn[i]) tarjan(i);        }        cur = res;        for(int i=1;i<=n;i++){            numA[bel[i]]++;        }        //A环找完        init(m);        for(int i=1;i<=m;i++){            G[b[i]].push_back(i);        }        for(int i=1;i<=m;i++){            if(!dfn[i]) tarjan(i);        }        for(int i=1;i<=m;i++){            numB[bel[i]]++;        }        int s[maxn] = {0};        for(int i=1;i<=m;i++){            s[numB[i]] += numB[i];        }        //B环找完.        ll ans[maxn] = {0};        for(int i=1;i<=cur;i++){   //循环节.            for(int j=1;j<=sqrt(numA[i]);j++){   //循环节因子.                if(numA[i] % j == 0){                    ans[i] += s[j];                    if(numA[i] / j != j)                        ans[i] += s[numA[i] / j];                }            }        }        printf("Case #%d: ",cas++);        ll xx = ans[1];        for(int i=2;i<=cur;i++){            xx *= ans[i];            xx %= mod;        }        printf("%lld\n",xx);    }}int main(){    int t = 1 ;    //scanf("%d",&t);    while(t--){    //    printf("Case %d: ", cas++);        solve();        //printf("\n");    }}
阅读全文
0 0
原创粉丝点击