問題を解決するためのHNCPC2019部分

ProblemSet

出席の問題は書いていません。

C.明確なサブストリング

私が学んだときにサフィックスオートマトンは、再びGUGU区のそれを書きます。

D.剰余ナイン

セット\(DP [I] [J ] [K] \) 充填フロント表す\(Iは\)この因子は数である3を含む最近の位置、\(Kを\) 次の最も近い\(J \)プログラムの数。

だから、状態の場合は、各ポイントの最近の出会いのニーズがエンドポイントの制限を左見つけ、(ないデフォルトでは0である)\(DP [I] [J] [k]が\) jおよびkを満たす私は最近、最後に残っていますポイントは、状態が正当である、I + 1ビットが設けられた検討(\ \ {0,9 \} \)または(\ \ {3,6 \} \)または(\ \ {1、2、 4、5、7、8 \} \) ブラシテーブルに移しました。

コード

#include <iostream>
#include <cstring>
#include <cstdio>

using namespace std;

void chkmax(int &x, int y) { x < y ? x = y : 0; }

const int maxN = 50;
const int mod = (int) 1e9 + 7;

int n, m;
int L[maxN + 2];
int dp[maxN + 2][maxN + 2][maxN + 2];

void pls(int &x, int y) 
{
    x += y;
    if (x >= mod) x -= mod;
    if (x < 0) x += mod;
}

int main()
{
    freopen("D.in", "r", stdin);
    freopen("D.out", "w", stdout);

    while (scanf("%d%d", &n, &m) != EOF)
    {
        memset(L, 0, sizeof L);
        for (int i = 1; i <= m; ++i)
        {
            int l, r;
            scanf("%d%d", &l, &r);
            chkmax(L[r], l);
        }
        memset(dp, 0, sizeof dp);
        dp[0][0][0] = 1;
        for (int i = 0; i < n; ++i)
            for (int j = 0; j <= i; ++j)
                for (int k = j; k <= i; ++k)
                {
                    if (!dp[i][j][k]) continue;
                    if (L[i] <= j and L[i] <= k)
                    {
                        int curr = dp[i][j][k];
                        // {1, 2, 4, 5, 7, 8}
                        pls(dp[i + 1][j][k], 6ll * curr % mod); 
                        // {3, 6}
                        pls(dp[i + 1][k][i + 1], 2ll * curr % mod);
                        // {0, 9}
                        pls(dp[i + 1][i + 1][i + 1], 2ll * curr % mod);
                    }
                }
        int ans = 0;
        for (int i = L[n]; i <= n; ++i)
            for (int j = i; j <= n; ++j)
                pls(ans, dp[n][i][j]);
        printf("%d\n", ans);
    }
}

G.辞書式

問題への参照溶液のZsy:

任意の2つの隣接する行が満たさ満たしている場合、長い線は、隣接する二つの限界を考慮見つけることは困難で、それはすなわち、正当なものである(\ \私が\ [FORALLにおいて、j個の\ [2、M] - 1 1、N] 、\存在K <Jの\)を満足\([I] [J ]> [I + 1] [J] \) と\([I] [K ] <[I + 1] [K] \) 。

この制限を抽象化することができる\(1 \ Iル\ N-LE - 。1 \)、\ (\ {1,2 \ cdotsのM \} \) 2つのサブセット\([I] \)\(B [ I]は\) 最終的な配置満たすために必要な\(B [i]は\)少なくとも一つの各要素\([I] \)の要素の後ろにあります。

制限は、数0のときBがセットAを制限するように設定されているので、各ドット(列)は、数がBの数を含んで設定される、いくつかの制限となり、我々が構成に追加することができインチ

4 3 3
1 5 1
1 5 1
3 5 2

上記の例:\([1] = \ {2 \}、A [2] = \ {\}、[3] = \ {1,3 \} \)\(Bの[1] = \ {1,3 \}、B [2] = \ {\}、B [3] = \ {\} \)

我々は、コンフィギュレーションをバックアップする前部を配置する場合の構成\([X] \)番号は、対応する\(B [X] \)最小見つけることはたびに-1を含有するカラムを、制限します列0の数を制限するために、最終的な配置と現在の構成は、それが中に、次いである([X] \)\セットに対応する\(B [X] \)制限列セット-1、次に\ ([X] \)\(B [x]は\)削除。

コード

#include <iostream>
#include <cstring>
#include <cstdio>
#include <vector>

using namespace std;

const int maxN = 2000;

int n, m, a[maxN + 2][maxN + 2];
int limits[maxN + 2];
vector<int> B_to_Lie[maxN + 2];
vector<int> Lie_to_A[maxN + 2];

int main()
{
    freopen("G.in", "r", stdin);
    freopen("G.out", "w", stdout);

    while (scanf("%d%d", &n, &m) != EOF)
    {
        memset(limits, 0, sizeof limits);
        for (int i = 1; i < n; ++i) B_to_Lie[i].clear();
        for (int i = 1; i <= m; ++i) Lie_to_A[i].clear();

        for (int i = 1; i <= n; ++i)
            for (int j = 1; j <= m; ++j)
                scanf("%d", &a[i][j]);

        for (int i = 1; i < n; ++i)
            for (int j = 1; j <= m; ++j)
                if (a[i][j] < a[i + 1][j])
                    Lie_to_A[j].push_back(i);
                else if (a[i][j] > a[i + 1][j])
                {
                    limits[j]++;
                    B_to_Lie[i].push_back(j);
                }

        int tot = 0;
        static int ans[maxN + 2];
        
        for (int t = 1; t <= m; ++t)
        {
            int p = 0;
            for (int i = 1; i <= m; ++i)
                if (!limits[i])
                {
                    p = i;
                    limits[i] = 0x3f3f3f3f;
                    break;
                }
            if (!p)
                break;
            ans[++tot] = p;
            for (int i = 0; i < (int) Lie_to_A[p].size(); ++i)
            {
                int A = Lie_to_A[p][i];
                for (int j = 0; j < (int) B_to_Lie[A].size(); ++j)
                {
                    int Lie = B_to_Lie[A][j];
                    limits[Lie]--;
                }
                B_to_Lie[A].clear();
            }
            Lie_to_A[p].clear();
        }
        if (tot == m)
            for (int i = 1; i <= m; ++i)
                if (i != m) printf("%d ", ans[i]);
                else printf("%d\n", ans[i]);
        else 
            puts("-1");
    }
    return 0;
}

H.は有向グラフ

集合\(E [U] \)を介して所望の無制限の歩行の\(U \) この場合に何回、後のn + 1 ... N + mは回数であるため、それほどN + 1 ... N + M点は確率が期待されます。

式には、高い消光を挙げることができます。

コード

#include <iostream>
#include <cstring>
#include <cstdio>

#define LL long long

using namespace std;

const int maxN = 500;
const int mod = (int) 1e9 + 7;

LL qpow(LL a, LL b)
{
    LL ans = 1;
    while (b) 
    {
        if (b & 1)
            ans = ans * a % mod;
        a = a * a % mod;
        b >>= 1;
    }
    return ans;
}

int n, m;
int a[maxN + 2][maxN + 2], P[maxN + 2][maxN + 2];

int main()
{
    freopen("H.in", "r", stdin);
    freopen("H.out", "w", stdout);
    while (scanf("%d%d", &n, &m) != EOF)
    {
        memset(a, 0, sizeof a);
        memset(P, 0, sizeof P);
        int inv = qpow(10000, mod - 2);
        for (int i = 1; i <= n; ++i)
            for (int j = 1; j <= n + m; ++j)
            {
                scanf("%d", &P[i][j]);
                P[i][j] = 1ll * P[i][j] * inv % mod;
            }
        for (int i = 1; i <= n; ++i)
        {
            for (int j = 1; j <= n; ++j)
            {
                a[i][j] = P[j][i];
                if (i == j) a[i][i]--;
                (a[i][j] += mod) %= mod;
            }
            if (i == 1) a[1][n + 1] = mod - 1;
            else a[i][n + 1] = 0;
        }
        for (int i = 1; i <= n; ++i)
        {
            for (int j = 1; j <= n; ++j)
                if (i != j)
                {
                    int mul = 1ll * a[j][i] * qpow(a[i][i], mod - 2) % mod;
                    for (int k = 1; k <= n + 1; ++k)
                    {
                        a[j][k] -= 1ll * a[i][k] * mul % mod;
                        a[j][k] %= mod;
                        (a[j][k] += mod) %= mod;
                    }
                }
        }
        for (int i = 1; i <= n; ++i)
            a[i][n + 1] = (1ll * a[i][n + 1] * qpow(a[i][i], mod - 2) % mod + mod) % mod;
        for (int i = 1 + n; i <= n + m; ++i)
        {
            int ans = 0;
            for (int j = 1; j <= n; ++j)
                (ans += 1ll * a[j][n + 1] * P[j][i] % mod) %= mod;
            printf("%d", (ans + mod) % mod);
            if (i != n + m) 
                putchar(' ');
        }
        putchar('\n');
    }
}

タプルのJ.パリティ(簡単)

まず、一緒に個々のラインアップの寄与を考慮してください。

次の行は考慮することです。

要件およびxは2進数の1の奇数、セットされているので\(F [i]が[S ] \) であると考えられる\(X \) iビット前に、行番号1における各進数Sパリティのバイナリ状態は答えに貢献します。

ため\(3 ^ X \)を転送する時間を乗じたバイナリ和X、に分割されます。

コード

#include <iostream>
#include <cstdio>
#include <cstring>

using namespace std;

const int maxN = (int) 1e4, maxM = 30, maxK = 30;
const int mod = (int) 1e9 + 7;

int n, m, k;
int b[maxK + 2], pw[maxK + 2];
int a[maxN + 2][maxM + 2];
int f[maxK + 2][1 << maxM];

int main()
{
    freopen("J.in", "r", stdin);
    freopen("J.out", "w", stdout);
    while (scanf("%d%d%d", &n, &m, &k) != EOF)
    {
        for (int i = 1; i <= n; ++i)
            for (int j = 0; j < m; ++j)
                scanf("%d", &a[i][j]);
        int ans = 0;
        for (int t = 1; t <= n; ++t)
        {
            for (int i = 0; i <= k; ++i)
            {
                b[i] = 0;
                for (int j = 0; j < m; ++j)
                    b[i] |= (a[t][j] >> i & 1) << j;
            }
            pw[0] = 3;
            for (int i = 1; i <= k; ++i)
                pw[i] = 1ll * pw[i - 1] * pw[i - 1] % mod;
            memset(f, 0, sizeof f);
            f[0][0] = 1;
            for (int i = 0; i < k; ++i)
                for (int S = 0; S < (1 << m); ++S)
                    if (f[i][S])
                    {
                        (f[i + 1][S ^ b[i]] += 1ll * f[i][S] * pw[i]) %= mod;
                        (f[i + 1][S] += f[i][S]) %= mod;
                    }
            (ans += f[k][(1 << m) - 1]) %= mod;
        }
        printf("%d\n", (ans + mod) % mod);
    }
}

K.二重にリンクされたリストの演習

両端キューヒューリスティックマージ。

コード

#include <iostream>
#include <queue>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <fstream>

typedef long long LL;
typedef unsigned long long uLL;

#define SZ(x) ((int)x.size())
#define ALL(x) (x).begin(), (x).end()
#define MP(x, y) std::make_pair(x, y)
#define DE(x) cerr << x << endl;
#define debug(...) fprintf(stderr, __VA_ARGS__)
#define GO cerr << "GO" << endl;
#define rep(i, a, b) for (register int (i) = (a); (i) <= (b); ++(i))

using namespace std;

inline void proc_status()
{
    ifstream t("/proc/self/status");
    cerr << string(istreambuf_iterator<char>(t), istreambuf_iterator<char>()) << endl;
}
inline int read() 
{
    register int x = 0; register int f = 1; register char c;
    while (!isdigit(c = getchar())) if (c == '-') f = -1;
    while (x = (x << 1) + (x << 3) + (c xor 48), isdigit(c = getchar()));
    return x * f;
}
template<class T> inline void write(T x) 
{
    static char stk[30]; static int top = 0;
    if (x < 0) { x = -x, putchar('-'); }
    while (stk[++top] = x % 10 xor 48, x /= 10, x);
    while (putchar(stk[top--]), top);
}
template<typename T> inline bool chkmin(T &a, T b) { return a > b ? a = b, 1 : 0; }
template<typename T> inline bool chkmax(T &a, T b) { return a < b ? a = b, 1 : 0; }

const int maxN = 2e5;

deque<int> q[maxN + 2];
int n, m;
bool rev[maxN + 2];

int size(int x) { return q[x].size(); }

int front(int x)
{
    if (rev[x]) return q[x].back();
    else return q[x].front();
}

int back(int x)
{
    if (rev[x]) return q[x].front();
    else return q[x].back();
}

void push_front(int x, int y)
{
    if (rev[x]) q[x].push_back(y);
    else q[x].push_front(y);
}

void push_back(int x, int y)
{
    if (rev[x]) q[x].push_front(y);
    else q[x].push_back(y);
}

void pop_back(int x)
{
    if (rev[x]) q[x].pop_front();
    else q[x].pop_back();
}

void pop_front(int x)
{
    if (rev[x]) q[x].pop_back();
    else q[x].pop_front();
}

int main() 
{
#ifndef ONLINE_JUDGE
    freopen("K.in", "r", stdin);
    freopen("K.out", "w", stdout);
#endif
    while (scanf("%d%d", &n, &m) != EOF)
    {
        for (int i = 1; i <= n; ++i)
            q[i].clear(), q[i].push_front(i), rev[i] = 0;
        for (int i = 1; i <= m; ++i)
        {
            int a, b;
            scanf("%d%d", &a, &b);
            if (q[a].size() > q[b].size())
            {
                while (size(b))
                {
                    push_back(a, front(b));
                    pop_front(b);
                }
                rev[a] ^= 1;
            } else 
            {
                while (size(a))
                {
                    push_front(b, back(a));
                    pop_back(a);
                }
                rev[a] = rev[b] ^ 1;
                swap(q[a], q[b]);
            }
        }
        printf("%d", q[1].size());
        if (rev[1]) { while (q[1].size()) printf(" %d", q[1].back()), q[1].pop_back(); }
        else { while (q[1].size()) printf(" %d", q[1].front()), q[1].pop_front(); }
        puts("");
    }
    return 0;
}

おすすめ

転載: www.cnblogs.com/cnyali-Tea/p/11443455.html