コード特技(UVA-11754、CRT +形而上学的暴力)

1.タイトルリンク:

グレープ-11754

2.トピックの主なアイデア:

最初の行の2つの整数CとS。

次のC行では、各行は2つの整数X [i]、K [i]で始まり、次にK [i]整数Y [i] [1 ... K [i]]

NがC条件を満たすような正の整数Nがあることを示します。N%X [i]はY [i] [1 ... K [i]]の中の数値です。

最小の最初のS個の正の整数Nを出力します。

3.分析:

Yのすべての組み合わせを列挙してから、CRTブルートフォースソリューションを列挙することは簡単に考えられます。

しかし、K [i]の積が大きい場合、これは間違いなくTLEを生成します...

だからここに形而上学的な暴力的な解決策があります。

K [i] / X [i]を最小化する添え字iを見つけ、N = Y [i] [j] + X [i] * tを列挙して、1つずつ確認します。

これが可能である理由について話しましょう。

K [i]の積が比較的大きいため、候補番号のセットが多くなります。Nを選択すると、チェックが成功する可能性が高くなります。

これが、K [i] / X [i]を最小化する添え字を選択する理由でもあります。K[i]が小さいと、列挙Yの数が少なくなり、X [i]が大きいとNの増加が速くなります。

大きな男の説明を投稿する

もう1つの質問は、K [i]の積が小さい場合にNを直接列挙できない理由です。

実際、前述のように、K [i]の積が小さい場合、候補番号のセットは少なく、チェックが成功する可能性は低くなります。

たとえば、このデータセット

3 10
1007 1 1006
1001 1 991
1003 1 1001

実際の測定後、1e7スケール×Nを列挙する必要があります。これは間違いなくTLEになります。

だから形而上学的~~

4.コードの実装:

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;

const int M = (int)1e1;
const int N = (int)1e2;

int c, s;
int x[M + 5];
int k[M + 5];
int y[M + 5][N + 5];
int a[M + 5];
vector <int> vx;
set <int> st[M + 5];

void init()
{
    for(int i = 1; i <= c; ++i)
    {
        st[i].clear();
        for(int j = 1; j <= k[i]; ++j)
        {
            st[i].emplace(y[i][j]);
        }
    }
}

bool check(ll n)
{
    for(int i = 1; i <= c; ++i)
    {
        if(!st[i].count(n % x[i]))
            return 0;
    }
    return 1;
}

void work1(int idx)
{
    init();
    ll n;
    for(int i = 0; s; ++i)
    {
        for(int j = 1; j <= k[idx]; ++j)
        {
            n = y[idx][j] + 1ll * x[idx] * i;
            if(n == 0)  continue;
            if(!check(n))   continue;
            printf("%lld\n", n);
            if(!--s)    break;
        }
    }
}

ll exgcd(ll a, ll b, ll& x, ll& y)
{
    if(b == 0)
    {
        x = 1;
        y = 0;
        return a;
    }
    ll d = exgcd(b, a % b, x, y);
    ll z = x;
    x = y;
    y = z - y * (a / b);
    return d;
}

int crt()
{
    ll res = 0, m = 1, M, t, y;
    for(int i = 1; i <= c; ++i)
        m *= x[i];
    for(int i = 1; i <= c; ++i)
    {
        M = m / x[i];
        exgcd(M, x[i], t, y);
        res = (res + a[i] * M % m * t % m) % m;
    }
    return (res + m) % m;
}

void dfs(int u)
{
    if(u == c + 1)
    {
        vx.push_back(crt());
        return;
    }
    for(int i = 1; i <= k[u]; ++i)
    {
        a[u] = y[u][i];
        dfs(u + 1);
    }
}

void work2()
{
    vx.clear();
    dfs(1);
    sort(vx.begin(), vx.end());
    ll m = 1;
    for(int i = 1; i <= c; ++i)
        m *= x[i];
    for(int i = 0; s; ++i)
    {
        for(auto x: vx)
        {
            ll n = x + m * i;
            if(n == 0)  continue;
            printf("%lld\n", x + m * i);
            if(!--s)    break;
        }
    }
}

int main()
{
    while(~scanf("%d %d", &c, &s) && (c + s))
    {
        int idx = 1;
        ll mul_k = 1;
        for(int i = 1; i <= c; ++i)
        {
            scanf("%d %d", &x[i], &k[i]);
            mul_k *= k[i];
            idx = (k[idx] * x[i] > k[i] * x[idx] ? i : idx);
            for(int j = 1; j <= k[i]; ++j)
                scanf("%d", &y[i][j]);
            sort(y[i] + 1, y[i] + k[i] + 1);
        }
        if(mul_k > (int)1e4)    work1(idx);
        else                    work2();
        puts("");
    }
    return 0;
}

 

おすすめ

転載: blog.csdn.net/The___Flash/article/details/104484740