【搜索】【Meet in the middle】【Noi2001】方程的解数

Description

已知一个n元高次方程:

其中:x1, x2,...,xn是未知数,k1,k2,...,kn是系数,p1,p2,...pn是指数。且方程中的所有数均为整数。
假设未知数1 <= xi <= M, i=1,,,n,求这个方程的整数解的个数。
1 <= n <= 6;1 <= M <= 150。

方程的整数解的个数小于231。
★本题中,指数Pi(i=1,2,...,n)均为正整数。

Input

第1行包含一个整数n。第2行包含一个整数M。第3行到第n+2行,每行包含两个整数,分别表示ki和pi。两个整数之间用一个空格隔开。第3行的数据对应i=1,第n+2行的数据对应i=n。

Output

仅一行,包含一个整数,表示方程的整数解的个数。

Sample Input

3
150
1  2
-1  2
1  2

Sample Output

178

分析

首先考虑暴力解法

搜索树一共六层,每层枚举1 ~ 150作为解

这样下来状态数就是150 ^ 6 = 11390625000000

实在是太过庞大

所以考虑meet in the middle 优化

这样一来状态数就变成了150 ^ 3 = 3375000

这道题就可以做了

我们把式子分为前半段和后半段分别处理出所有值

然后统计所有两段之和为0的方案数

双指针扫描法(自己起的名字。嘿嘿嘿)

代码

#include
#include
#include
using namespace std;

int n, m;
int k[17], p[17];
int a[4000010], b[4000010];
int cnta, cntb;
long long ans;

inline int read() {
    int x = 0, f = 1; char ch = getchar();
    while (ch < '0' || ch > '9') {if (ch == '-') f = -1; ch = getchar();}
    while (ch >= '0' && ch <= '9') {x = (x << 1) + (x << 3) + (ch ^ 48); ch = getchar();}
    return x * f;
}

int pow(int aa, int bb) {
    int mul = 1;
    while (bb) {
	if (bb & 1) mul *= aa;
	aa *= aa;
	bb >>= 1;
    }
    return mul;
}

void dfs1(int dep, int sum) {
    if (dep > n / 2) {
	a[++cnta] = sum;
	return;
    }
    for (int i = 1; i <= m; i++) dfs1(dep + 1, sum + k[dep] * pow(i, p[dep]));
}

void dfs2(int dep, int sum) {
    if (dep > n) {
	b[++cntb] = sum;
	return;
    }
    for (int i = 1; i <= m; i++) dfs2(dep + 1, sum + k[dep] * pow(i, p[dep]));
}

int main() {
    n = read(), m = read();
    for (int i = 1; i <= n; i++) k[i] = read(), p[i] = read();
    dfs1(1, 0);
    dfs2(n / 2 + 1, 0);
    int j = cntb;
    sort(a + 1, a + cnta + 1);
    sort(b + 1, b + cntb + 1);
    for (int i = 1; i <= cnta; i++) {
	while (j && a[i] + b[j] > 0) j--;
	if (!j) break;
	if (a[i] + b[j] == 0) {
	    int ca = 1, cb = 1;
	    while (i < cnta && a[i] == a[i + 1]) ca++, i++;
	    while (j > 1 && b[j] == b[j - 1]) cb++, j--;
	    ans += ca * cb;
	}
    }
    printf("%lld", ans);
}


 

你可能感兴趣的:(搜索)