題名
回答
素朴な列挙方程式の解が判断され、時間計算量は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 = 1∑⌊ N / 2 ⌋k私バツ私p私=−私は= ⌊ N / 2 ⌋ + 1∑Nk私バツ私p私左項の可能な解は前処理されて順序付けられ、右項の可能な解が列挙され、方程式を満たす左項の数が二分法で解かれます。合計時間計算量O(MN /2log(MN / 2))O(M ^(N / 2)\ log(M ^(N / 2)))O (MN / 2lo g(MN / 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;
}