304. [NOI2001] 方程的解数
★★☆ 输入文件:equation1.in
输出文件:equation1.out
简单对比
时间限制:3 s 内存限制:64 MB
问题描述
已知一个n元高次方程:
k1xp11+k2xp22+⋯+ knxpnn=0
其中:x1, x2, …,xn是未知数,k1,k2,…,kn是系数,p1,p2,…pn是指数。且方程中的所有数均为整数。
假设未知数1≤ xi ≤M, i=1,,,n,求这个方程的整数解的个数。
输入文件
文件的第1行包含一个整数n。第2行包含一个整数M。第3行到第n+2行,每行包含两个整数,分别表示ki和pi。两个整数之间用一个空格隔开。第3行的数据对应i=1,第n+2行的数据对应i=n。
输出文件
文件仅一行,包含一个整数,表示方程的整数解的个数。
输入样例
3
150
1 2
-1 2
1 2
输出样例
178
约束条件
1<=n<=6;1<=M<=150;
|k1Mp1|+|k2Mp2|+⋯+|knMpn|<231
方程的整数解的个数小于2^31。
★本题中,指数Pi(i=1,2,……,n)均为正整数。
思路:meet in the middle。
计算满足 a+b+c+d+e+f=0 的数的个数
可以算 (a+b+c)+ (d+e+f) = 0
dfs两次,每次dfs一半
结果用双指针逼近法、乘法原理 O(n) 处理
dfs中 now代表计算到第几个数 ,tot代表当前的总和,tmp存储和是几,sum存储有几个和。
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #define MAXN 3442951 using namespace std; int n,m,half,last,sum1,sum2,ans; int k[10],p[10],tmp1[MAXN],tmp2[MAXN]; int fastpow(int a,int b){ int s=1; while(b){ if(b&1) s=s*a; a=a*a; b>>=1; } return s; } void dfs(int now,int tot,int *tmp,int &sum){ if(now>last){ tmp[++sum]=tot; return ; } for(int i=1;i<=m;i++) dfs(now+1,tot+k[now]*fastpow(i,p[now]),tmp,sum); } void work(){ int cnt1,cnt2,j=sum2; sort(tmp1+1,tmp1+1+sum1); sort(tmp2+1,tmp2+1+sum2); for(int i=1;i<=sum1;i++){ while(j&&tmp1[i]+tmp2[j]>0) j--; if(!j) break; if(tmp1[i]+tmp2[j]==0){ cnt1=cnt2=1; while(i<sum1&&tmp1[i]==tmp1[i+1]) cnt1++,i++; while(j>1&&tmp2[j]==tmp2[j-1]) cnt2++,j--; ans+=cnt1*cnt2; } } } int main(){ freopen("equation1.in","r",stdin); freopen("equation1.out","w",stdout); scanf("%d%d",&n,&m); half=n/2; for(int i=1;i<=n;i++) scanf("%d%d",&k[i],&p[i]); last=half; dfs(1,0,tmp1,sum1); last=n; dfs(half+1,0,tmp2,sum2); work(); cout<<ans; }