P5691 [NOI2001]半分の列挙

題名

ポータルP5691 [NOI2001]方程式の解の数

回答

素朴な列挙方程式の解が判断され、時間計算量はO(MN)O(M ^ N)です。O MN列挙を半分に考え、項を移動します
∑ i =1⌊N/2⌋kixipi= − ∑ i =⌊N/2⌋+ 1 N kixipi \ sum \ limits_ {i = 1} ^ {\ lfloor N / 2 \ rfloor} k_ix_i ^ {p_i} =-\ sum \ limits_ {i = \ lfloor N / 2 \ rfloor + 1} ^ {N} k_ix_i ^ {p_i}i = 1N / 2 kバツp=私は= N / 2 + 1Nkバツp左項の可能な解は前処理されて順序付けられ、右項の可能な解が列挙され、方程式を満たす左項の数が二分法で解かれます。合計時間計算量O(MN /2log⁡(MN / 2))O(M ^(N / 2)\ log(M ^(N / 2)))O MN / 2lo gMN / 2

#include <bits/stdc++.h>
using namespace std;
const int maxn = 8, maxm = 155, maxt = maxm * maxm * maxm;
int N, M, na, nb, K[maxn], P[maxn], A[maxt], B[maxt];

int qpow(int x, int n)
{
    
    
    int res = 1;
    while (n)
    {
    
    
        if (n & 1)
            res *= x;
        x *= x, n >>= 1;
    }
    return res;
}

void dfs(int s, int t, int x, int &n, int *a)
{
    
    
    if (s > t)
    {
    
    
        a[++n] = x;
        return;
    }
    for (int i = 1; i <= M; ++i)
        dfs(s + 1, t, x + K[s] * qpow(i, P[s]), n, a);
}

int main()
{
    
    
    scanf("%d%d", &N, &M);
    for (int i = 1; i <= N; ++i)
        scanf("%d%d", K + i, P + i);
    int h = N >> 1;
    dfs(1, h, 0, na, A), dfs(h + 1, N, 0, nb, B);
    sort(A + 1, A + na + 1);
    int res = 0;
    for (int i = 1; i <= nb; ++i)
        res += upper_bound(A + 1, A + na + 1, -B[i]) - lower_bound(A + 1, A + na + 1, -B[i]);
    printf("%d\n", res);
    return 0;
}

おすすめ

転載: blog.csdn.net/neweryyy/article/details/114340014