P5691 [NOI2001] 折半枚举

题意

传送门 P5691 [NOI2001] 方程的解数

题解

朴素枚举方程的解进行判断,时间复杂度 O ( M N ) O(M^N) O(MN)。考虑折半枚举,将方程移项
∑ i = 1 ⌊ N / 2 ⌋ k i x i p i = − ∑ i = ⌊ N / 2 ⌋ + 1 N k i x i p i \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/2kixipi=i=N/2+1Nkixipi 预处理左项的可能解并使之有序,枚举右项的可能解,同时二分求解满足方程的左项数量。总时间复杂度 O ( M N / 2 log ⁡ ( M N / 2 ) ) O(M^{N/2}\log (M^{N/2})) O(MN/2log(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;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值