FJWC2020 Day4の問題解決

T1

説明

既知の\(A G_0 = \)、\ (G_1 = B \) および持ってい

\ [G_N = 3g_ {N-1} -g_ {N-2}(N> 1)\]

既知の(N-F_ {、} = 0 N- \)\、および

\ [F_ {N、K} = F_ {G_N、K-1}(K> 0)\]

与えられた(A、B、N、K 、P \)\ 値は、取得\(F_ {N、K} \ BMOD P \) の結果を。

\(0≤、B <P \)\(1≤T≤\ 1000)\(1≤N、P≤10 ^ 9 \)\(1≤K≤100 \)

解決

まず考える方法BEG \(G_N G_ {} \)

\(G_N \)大きいかもしれない、求めて検討し\(G_N \)ループ部。

\(F_nは\)のフィボナッチ数である\(N- \) プレイテーブルの中に見出すことができるキー(G_N = F_ {2N-F_ {B}×2(N - 1)}×A \)\

だから、(G \)\同じ行為フィボナッチ数列とループ部。

具体的には、フィボナッチ列モードに注意してください\(P \)としてセクションの意義の範囲内で、最低循環を(\ PI(P-)\)\

もし\(P = \ 1 prod_ {I} = ^ {^ wp_i a_iを} \) 品質係数の分解)、その後:

\ [\ PI(P)= \テキスト{LCM} _ {i = 1} ^ W(H(P_I)P_I ^ {a_iを-1})\]

間で

\ [H(P)= \ {ケースを}開始3&P = 2 \\ 20&P = 5 \\ P-1およびP =±1 \ pmod5 \\ 2P + 2&P =±2 \ pmod5 \\ \端{ケース} \]

あまりにも証明した(I)錯体(も)その他(ない)(意志)、そこに開始されません。

時間複雑\(O(生きることができます)\)

コード

#include <bits/stdc++.h>

using namespace std;

#define ll long long

template <class t>
inline void read(t & res)
{
    char ch;
    while (ch = getchar(), !isdigit(ch));
    res = ch ^ 48;
    while (ch = getchar(), isdigit(ch))
    res = res * 10 + (ch ^ 48);
}

template <class t>
inline void print(t x)
{
    if (x > 9) print(x / 10);
    putchar(x % 10 + 48);
}

const int e = 1e7 + 5;

struct matrix
{
    int r, c;
    ll a[2][2];
};

ll now_p, A, B;
int L = 1e7, cnt, pri[e];
bool bo[e];

inline void init()
{
    int i, j;
    for (i = 2; i <= L; i++)
    {
        if (!bo[i]) pri[++cnt] = i;
        for (j = 1; j <= cnt && i * pri[j] <= cnt; j++)
        {
            bo[i * pri[j]] = 1;
            if (i % pri[j] == 0) break;
        }
    }
}

inline ll mul(ll x, ll y)
{
    ll res = x * y - (ll)((long double)x * y / now_p + 1e-8) * now_p;
    return res < 0 ? res + now_p : res; 
}

inline void add(ll &x, ll y)
{
    (x += y) >= now_p ? x -= now_p : 0; 
}

inline matrix operator * (matrix a, matrix b)
{
    matrix c;
    memset(c.a, 0, sizeof(c.a));
    int i, j, k;
    for (i = 0; i < a.r; i++)
    for (k = 0; k < b.r; k++)
    for (j = 0; j < b.c; j++)
    add(c.a[i][j], mul(a.a[i][k], b.a[k][j]));
    c.r = a.r; c.c = b.c;
    return c;
}

inline ll calc(ll n)
{
    matrix res, c;
    res.a[1][0] = res.a[1][1] = 0;
    res.a[0][0] = A % now_p; res.a[0][1] = B % now_p;
    res.r = 1; res.c = 2;
    c.a[0][0] = 0; c.a[0][1] = now_p - 1;
    c.a[1][0] = 1; c.a[1][1] = 3;
    c.r = c.c = 2;
    while (n)
    {
        if (n & 1) res = res * c;
        n >>= 1;
        c = c * c;  
    }
    return res.a[0][0];
}

inline ll get_g(ll p)
{
    return p == 2 ? 3 : p == 5 ? 20 : p % 5 == 1 ? p - 1 : p % 5 == 4 ? p - 1 : 2 * p + 2;
}

inline ll lcm(ll a, ll b)
{
    return a / __gcd(a, b) * b;
}

inline ll find(ll x)
{
    ll res = 1;
    int i;
    ll s = sqrt(x);
    for (i = 1; i <= cnt && pri[i] <= s; i++)
    if (x % pri[i] == 0)
    {
        int p = pri[i];
        ll t = get_g(p);
        while (x % p == 0) 
        {
            x /= p; t *= p;
        }
        t /= p;
        res = lcm(res, t);
    }
    if (x != 1) res = lcm(res, get_g(x));
    return res;
}

inline ll solve(ll n, ll k, ll p)
{
    if (!k) return n % p;
    ll cir = find(p), nxt = solve(n, k - 1, cir);
    now_p = p;
    ll res = calc(nxt);
    return res;
}

inline void work()
{
    read(A); read(B);
    int n, k, p;
    read(n); read(k); read(p);
    ll ans = solve(n, k, p);
    print(ans); putchar('\n');
}

int main()
{
    freopen("hakugai.in", "r", stdin);
    freopen("hakugai.out", "w", stdout);
    init();
    int T;
    read(T);
    while (T--) work();
    fclose(stdin);
    fclose(stdout);
    return 0;
}

T2

説明

与えられた\(N- \)ツリーポイント、ポイントは番号が付けられ、\(0 \ -n-SIM。1 \)

あなたがすることができます(kは\)\操作を、各操作あなたは、エッジを追加し、いずれかの側の現在のツリーを削除することができます。あなたは、現在のマップの各操作の後に木のまま、エッジ後の各削除があることを確認する必要がありますすることができます追加され、同じ側縁を削除しました。

今、あなたはで、カウント\(K \)操作の後、多くの木が形成することができるかの合計があります。

側面が存在する場合に限り、異なる形式2つのツリーは、\((X、Y)\) ツリーに表示され、ツリーが他には表示されません。

\(1 \当量のn \ 50当量\)、\ (0 \当量のK <N \)

解決

タイトルすなわち尋ねる:多くの木が会う方法(K \)\元のツリーではなくエッジ。

定義エッジ\((U、V)\ ) 重み:もし\((U、V)\ ) 元のツリー内の重量\(1 \) そうでなければ\(X \)

私たちは、右側の積である需要(\ルのx ^ kの\ \ ) スパニングツリーの数。

製品の定義は、スパニングツリー重量の右側で、行列の値は、決定木定理重量およびすべてのスパニングツリーとすることができる、よく知られています。

この質問では、得られた重みの合計が含有されている\(X \に)\(N-1 \) 次多項式を(X ^ I \)\係数の重量である(I \)\のスパニングツリーの数、\(X- ^ 0 \ SIM X- ^ K \)係数の合計が答えです。

私が考える、直接多項式を見つけることは容易でないことが判明\(\テキスト{ラグランジュ} \ ) の補間、即ちよう\(X = 1 \ N-SIM \) 値は定理多項式行列ツリーを得ました。

全ての代替の得られた値(\ \テキスト{ラグランジュ} \ ) 補間式は、多項式を復元することができます。

時間複雑\(O(N ^ 4)\)

コード

#include <bits/stdc++.h>

using namespace std;

#define ll long long

template <class t>
inline void read(t & res)
{
    char ch;
    while (ch = getchar(), !isdigit(ch));
    res = ch ^ 48;
    while (ch = getchar(), isdigit(ch))
    res = res * 10 + (ch ^ 48);
}

const int e = 155, mod = 998244353;

bool bo[e][e];
int a[e][e], inv[e], ans, n, m, c[e], d[e], h[e];

inline void add(int &x, int y)
{
    (x += y) >= mod && (x -= mod);
}

inline void del(int &x, int y)
{
    (x -= y) < 0 && (x += mod);
}

inline int mul(int x, int y)
{
    return (ll)x * y % mod;
}

inline int ksm(int x, int y)
{
    int res = 1;
    while (y)
    {
        if (y & 1) res = mul(res, x);
        y >>= 1;
        x = mul(x, x);
    }
    return res;
}

inline int gauss(int n)
{
    int i, j, k, res = 1;
    for (i = 1; i < n; i++)
    {
        int x = 0;
        for (j = i; j <= n; j++)
        if (a[j][i])
        {
            x = j;
            break;
        }
        if (!x) continue;
        if (x != i) res = mod - res, swap(a[x], a[i]);
        int inv = ksm(a[i][i], mod - 2);
        for (j = i + 1; j <= n; j++)
        {
            int v = mul(a[j][i], inv);
            for (k = i; k <= n; k++) del(a[j][k], mul(a[i][k], v));
        }
    }
    for (i = 1; i <= n; i++) res = mul(res, a[i][i]);
    return res;
}

inline int calc(int bas)
{
    int i, j;
    for (i = 1; i <= n; i++)
    for (j = 1; j <= n; j++)
    a[i][j] = 0;
    for (i = 1; i < n; i++)
    for (j = i + 1; j <= n; j++)
    {
        int v = 1;
        if (!bo[i][j]) v = bas;
        add(a[i][i], v); add(a[j][j], v);
        del(a[i][j], v); del(a[j][i], v);
    }
    return gauss(n - 1);
}

int main()
{
    freopen("kaisou.in", "r", stdin);
    freopen("kaisou.out", "w", stdout);
    read(n); read(m);
    int i, x, j, k;
    inv[1] = 1;
    for (i = 2; i <= n; i++)
    {
        read(x);
        x++;
        bo[x][i] = bo[i][x] = 1;
        inv[i] = mul(mod - mod / i, inv[mod % i]);
    }
    for (i = 1; i <= n; i++)
    {
        int y = calc(i);
        for (j = 1; j <= n; j++) c[j] = 0;
        c[0] = 1;
        for (j = 1; j <= n; j++)
        if (j != i)
        {
            if (i < j) y = mul(y, mod - inv[j - i]);
            else y = mul(y, inv[i - j]);
            for (k = 0; k <= n; k++)
            {
                h[k] = mul(mod - j, c[k]);
                if (k) add(h[k], c[k - 1]);
            }
            for (k = 0; k <= n; k++) c[k] = h[k];
        }
        for (j = 0; j <= n; j++) add(d[j], mul(c[j], y));
    } 
    for (i = 0; i <= m; i++) add(ans, d[i]);
    cout << ans << endl;
    fclose(stdin);
    fclose(stdout);
    return 0;
}

T3

説明

加重側所与のツリーは、あなたが得る満足\(U \)\(V \)最短単純なパス内のすべてのエッジのエッジ加重\(\ mathrm {GCD} \ ) のを\( 1 \)に点\((U、V)( U <V)\) 数。

だけでなく、あなたが対処しなければならない、ということ\(Q \)各変更の後に動作して改正右側には、現在の状況の出力に答える必要があります。

\(2≤N≤10 ^ 5 \)\(Q≤100 \)\(1≤、W、X≤10 ^ 6 \)

解決

まず、\(ANS = \ sum_ {私は= 1} ^ {10 ^ 6} \ MU(I)F(I)\) ここで\(F(I)\)を表し\(\ mathrm {GCD} \ BMODをi)は、\ = 0のパスの数です。

その後、各エッジ((U、V、Wは\ )\) に分割される\((U、V、W_1)、(U、V、W_2)、\ DOTS、(U、V、W_k)\)

前記\(W_1 \ SIM w_k \)です\(W \)のすべてを満たす\(\ミュー\ NE 0 \ ) 除数。

だから今\(F(i)が\)へのすべての側面の権利である(\ I)\パスの数の。

各エッジが変更されたため(X \)\それぞれ時間考え、\(0 \ SIM Q \) (X \)\重み。

このつまり\(Q + 1 \)各エッジ上のすべての数に分割し、時間マーカー約重量時間。

側面のために修正されていない(X \)\、縁部の解体に時間である\( - 1 \) そこには常に発現されます。

次に、列挙の右側には、\(V \)の正しい値を計算\(V \)この側\(Q + 1 \)回答の寄与を。

変数を維持しながら具体的には、永続的な互いに素なセットを維持する\(SUM \)を、側面が正しいどのように多くの現在のフォームを前記(V \)\パス。

列挙時間\(T = -1 \ SIM Q \)

最初の\(T = -1 \)互いに素なセットを添加し、そして次に列挙される\(\ lceilを\)エッジの重みが存在しない\(V \)エッジ\(\ rfloor \)時間\(J \ )、\ (ANS [J] = SUM +×\ MU(V)\)

\(T \ GE 0 \)各時間\(Tは\)互いに素なセットが付加されている、の計算(ANS [T] \)\これらの側面の全てからの寄与、次いで濃縮調査後削除されました。

予想される時間複雑\(O((N-Q +)N-ログ\ S)\) \(S \)\(10 ^ 6 \)数平均除数内の数。

コード

#include <bits/stdc++.h>

using namespace std;

#define ll long long

template <class t>
inline void read(t & res)
{
    char ch;
    while (ch = getchar(), !isdigit(ch));
    res = ch ^ 48;
    while (ch = getchar(), isdigit(ch))
    res = res * 10 + (ch ^ 48);
}

template <class t>
inline void print(t x)
{
    if (x > 9) print(x / 10);
    putchar(x % 10 + 48);
}

const int e = 2e5 + 5, o = 1e6 + 5;

struct point
{
    int id, t;
    
    point(){}
    point(int _id, int _t) :
        id(_id), t(_t) {}
};
struct line
{
    int x, y, z;
}b[e], c[e];
bool bo[o];
int fa[e], sze[e], n, q, miu[o], L = 1e6, tim[e], vis[e];
ll ans[e], sum;
vector<int>stk, a[o];
vector<point>g[o], h[e];

inline void init()
{
    int i, j;
    for (i = 1; i <= L; i++) miu[i] = 1;
    for (i = 2; i <= L; i++)
    if (!bo[i])
    {
        miu[i] = -1;
        a[i].push_back(i);
        for (j = i << 1; j <= L; j += i)
        {
            bo[j] = 1;
            if (j / i % i == 0) miu[j] = 0;
            else miu[j] = -miu[j];
            a[j].push_back(i);
        }
    }
}

inline int find(int x)
{
    while (fa[x]) x = fa[x];
    return x;
}

inline void merge(int x, int y)
{
    int fx = find(x), fy = find(y);
    if (sze[fx] < sze[fy]) swap(fx, fy);
    fa[fy] = fx;
    sum += (ll)sze[fx] * sze[fy];
    sze[fx] += sze[fy];
    stk.emplace_back(fy);
}

inline void cut()
{
    int fy = stk.back(), fx = fa[fy];
    sze[fx] -= sze[fy];
    sum -= (ll)sze[fx] * sze[fy];
    fa[fy] = 0;
    stk.pop_back();
}

inline void ins(int x, int id, int t)
{
    point u = point(id, t);
    int s, len = a[x].size(), i, all = 1 << len;
    for (s = 0; s < all; s++)
    {
        int y = 1;
        for (i = 0; i < len; i++)
        if (s & (1 << i)) y *= a[x][i];
        g[y].emplace_back(u);
    }
}

inline void solve(int x)
{
    int i, j, len = g[x].size(), k;
    sum = 0;
    ll tot = 0;
    for (i = 0; i < len; i = j + 1)
    {
        j = i - 1;
        int now_t = g[x][i].t;
        while (j < len - 1 && g[x][j + 1].t == now_t)
        {
            j++;
            int pos = g[x][j].id;
            merge(b[pos].x, b[pos].y);
        }
        ll tmp = miu[x] * sum;
        if (now_t != -1)
        {
            ans[now_t] += tmp;
            for (k = i; k <= j; k++) cut();
        }
        else
        {
            for (k = j + 1; k < len; k++) vis[g[x][k].t] = x;
            for (k = 0; k <= q; k++)
            if (vis[k] != x) ans[k] += tmp;
        }
    }
    while (stk.size()) cut();
}

int main()
{
    freopen("atoranta.in", "r", stdin);
    freopen("atoranta.out", "w", stdout);
    init();
    read(n);
    int i, j;
    for (i = 1; i < n; i++) read(b[i].x), read(b[i].y), read(b[i].z);
    read(q);
    for (i = 1; i <= q; i++) read(c[i].x), read(c[i].y), tim[c[i].x] = i;
    for (i = 1; i < n; i++)
    if (!tim[i]) ins(b[i].z, i, -1);
    for (i = 1; i <= q; i++)
    {
        point u = point(c[i].x, c[i].y);
        for (j = i; j <= q; j++)
        if (j == i || c[j].x != c[i].x) h[j].emplace_back(u);
        else break;
        bool pd = 0;
        for (j = 1; j < i; j++)
        if (c[j].x == c[i].x)
        {
            pd = 1;
            break;
        }
        if (!pd)
        {
            point v = point(c[i].x, b[c[i].x].z);
            for (j = 0; j < i; j++) h[j].emplace_back(v);
        }
    }
    for (i = 0; i <= q; i++)
    {
        int len = h[i].size();
        for (j = 0; j < len; j++) ins(h[i][j].t, h[i][j].id, i);
    }
    for (i = 1; i <= n; i++) sze[i] = 1;
    for (i = 1; i <= L; i++)
    if (g[i].size()) solve(i);
    for (i = 0; i <= q; i++) print(ans[i]), putchar('\n');
    fclose(stdin);
    fclose(stdout);
    return 0;
}

おすすめ

転載: www.cnblogs.com/cyf32768/p/12324132.html