1. 程式人生 > >2017多校聯合第一場 1006題 hdu 6038 Function 迴圈節

2017多校聯合第一場 1006題 hdu 6038 Function 迴圈節

題目連結

題意:

Please calculate the quantity of different functions f satisfying that f(i)=bf(ai) for each i from 0 to n1.

(嗯...就是這樣)

思路:

首先可以將 a 陣列劃分成若干個迴圈節,每個節是相互獨立的,取其中一個節來看,令其長度為 l

顯然,由定義,該節就決定了 bx = y, by = z, ... bxx = x, 的這個迴圈節有多長,

再看 b,顯然我們應該將 b 的迴圈節 (設長度為 l0) 對應賦給上述迴圈節,至於長度呢,

1. l0 == l,顯然可行,總共有 l 種排列

2. l0 == 1, 顯然可行,注意到,此時第一個所賦的值決定了這 l 個式子的值,

即,若賦了 bx = x, 則接下來 l - 1 個式子必然也都是 bx = x, 因為第一個式子決定了 y = x, 繼續下去, z = y, 以此類推,

故對每種賦值方法,有且只有 1 種排列,

3. 根據第二點就很容易想到了,l 的所有約數長度的迴圈節都是可以的,並且第一整段 l0 長度的式子決定了後面若干個整段 l0 長度的式子(和第一整段均一模一樣),

故,一旦選定了b的某個迴圈節,且一旦確定了第一整段的排列,那麼長度為 l 的整段的排列都唯一確定了,

而第一整段的排列有 l0 種,

答案就是\prod_{i = 1}^{k} \sum_{j | l_i} {j \cdot cal_j}

i=1kjlijcalj,其中kk是置換aa中迴圈節的個數,l_ili表示置換aa中第ii個迴圈節的長度,cal_jcalj表示置換bb中長度為jj的迴圈節的個數。

官方題解

AC程式碼如下:

#include <cstdio>
#include <cmath>
#include <cstring>
#define mod 1000000007
#define maxn 100010
typedef long long LL;
int kas, a[maxn], b[maxn], n, m;
LL tot[maxn];
bool in[maxn];
void findcycle(int* a, int n) {
    for (int i = 0; i < n; ++i) {
        if (!in[i]) {
            in[i] = true;
            int ii = a[i], cnt = 1;
            while (ii != i) {
                in[ii] = true;
                ii = a[ii];
                ++cnt;
            }
            ++tot[cnt];
        }
    }
}
void work() {
    for (int i = 0; i < n; ++i) scanf("%d", &a[i]);
    for (int i = 0; i < m; ++i) scanf("%d", &b[i]);
    memset(tot, 0, sizeof(tot));
    memset(in, 0, sizeof(in));
    findcycle(b, m);
    memset(in, 0, sizeof(in));
    LL ans = 1;
    for (int i = 0; i < n; ++i) {
        if (!in[i]) {
            in[i] = true;
            int ii = a[i], cnt = 1;
            while (ii != i) {
//                printf("%d ", ii);
                in[ii] = true;
                ii = a[ii];
                ++cnt;
            }
//            printf("\n");
//            printf("%d %d\n", i, cnt);
            LL mul = 0;
            for (int j = 1; j <= cnt; ++j) {
                if (cnt % j == 0) {
                    mul += (tot[j] * j) % mod;
                    mul %= mod;
                }
            }
            ans *= mul;
            ans %= mod;
        }
    }

    printf("Case #%d: %lld\n", ++kas, ans % mod);
}
int main() {
    while (scanf("%d%d\n", &n, &m) != EOF) work();
    return 0;
}



怎麼說呢, 雖然有點馬後炮...

然而,這道題應該還是挺好做的

主要是當時太浮躁,

一來認為自己做不出來

二來被前面 02 WA了太多次很喪氣

三來感覺當時做得人並不太多(也不知哪來的錯覺...

四來就是其它一些瑣碎的原因了...

其實主要吧...還就是不想做(嘆氣

結束之後定下來想了一想,很快就想得八九不離十了...

嗯...挺遺憾的......

接下來都要振作起來啊