方程的解数
Problem
已知一个n元高次方程:
其中:x1, x2, …,xn是未知数,k1,k2,…,kn是系数,p1,p2,…pn是指数。且方程中的所有数均为整数。
假设未知数1≤ xi ≤M, i=1...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 ≤ n ≤ 6, 1 ≤ M ≤ 150
- |k1Mp1| + |k2Mp2| + ... + |knMpn| < 231
- 方程的整数解的个数小于231。
- 本题中,指数pi(i=1,2,……,n)均为正整数。
双向枚举似乎没什么好的优化,(正的放一边,负的放一边?也许会有效果),hash的写法能加速不少首先对于负数,是直接求余呢还是转化为正数再求余,对于这道题,后者比前者能快0.8s~1s其次,求MOD时,多判断一下是否大于MOD再求MOD会比直接求MOD快最后,把hash的添加和查找分开,不做无谓的添加,可以提高速度最最后,内联函数似乎也会快一点
- 求一个n元一次方程解的个数(解在一定范围内)枚举(裸搜)
- 单向枚举,150^6,无法接受
- 双向枚举+hash,150^3*hash的时间复杂度,2s左右过
- 双向枚举+二分查找,150^3*log(150^3)...还能接受,12s左右
- 双向枚举+排序+挨个对,复杂度同上,10s左右
双向枚举+排序+挨个对
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 10
#define M 160
#define MOD (150*150*160)
using namespace std;
int n,m,ans,tot,ntot;
int f[M][N];
int k[N],p[N];
//虽然叫hash,但其实不是hash
struct CHash
{
int num;
int tot;
bool operator<(const CHash b)const
{
return num<b.num;
}
} nhash[MOD],hash[MOD];
//快速幂
int quickPower(int a,int b)
{
int y = a, t = 1;
while( b )
{
if( b&1 ) t *= y;
y = y * y;
if( y == 1 ) return t;
b >>= 1;
}
return t;
}
//深搜
void dfs(int c,int num,int p)
{
if( c == p )
{
if( p < n )
{
nhash[tot++].num=num;
}
else
{
hash[ntot++].num=num;
}
}
else
{
for( int i = 1 ; i <= m ; i++ )
{
dfs( c+1, num+f[i][c] ,p);
}
}
}
//排序+合并
void solve(CHash nhash[],int &tot)
{
sort(nhash,nhash+tot);
int j = 0;
nhash[0].tot=1;
for(int i = 1; i<tot; i++)
{
if(nhash[i].num==nhash[j].num)
{
nhash[j].tot++;
}
else
{
nhash[++j].num=nhash[i].num;
nhash[j].tot=1;
}
}
tot=j+1;
}
int main()
{
while(scanf("%d %d",&n,&m)==2)
{
ans = 0;
for(int i = 0 ; i < n ; i ++ )
{
scanf("%d %d",k+i,p+i);
}
for(int i = 1 ; i <= m ; i ++ )
{
for( int j = 0 ; j < n ; j ++ )
{
f[ i ][ j ] = k[ j ] * quickPower( i , p[ j ] );
}
}
if( n==1 )
{
if( k[0] == 0 )
{
printf("%d\n",m);
}
else puts("0");
continue;
}
tot=0;
ntot=0;
dfs(0,0,n>>1);
solve(nhash,tot);
dfs(n>>1,0,n);
solve(hash,ntot);
int j = ntot - 1;
//前后合并
for(int i = 0 ; j>=0&&i < tot ; i++ )
{
while(j>0&&hash[j].num+nhash[i].num>0)j--;
if(hash[j].num+nhash[i].num==0)ans+=hash[j].tot*nhash[i].tot;
}
printf("%d\n",ans);
}
return 0;
}
双向枚举+hash:
#include <iostream>
#include <cstdio>
#include <cstring>
#define N 10
#define M 160
#define MOD 6313813
using namespace std;
int n,m,ans;
int f[M][N];
int k[N],p[N];
struct CHash
{
int num;
int tot;
}nhash[MOD];
int quickPower(int a,int b)
{
int y = a, t = 1;
while( b )
{
if( b&1 ) t *= y;
y = y * y;
if( y == 1 ) return t;
b >>= 1;
}
return t;
}
inline int hash(int k)
{
int y = (k<0?-k:k) % MOD; //优化1
while( nhash[ y ].tot && nhash[ y ].num != k )
{
y ++;
if( y >= MOD ) y %= MOD; //优化2
}
return y;
}
void dfs(int c,int num,int p)
{
if( c == p )
{
if( p < n )
{
int q = hash(num);
nhash[ q ].tot++;
nhash[ q ].num = num;//优化3
}else
{
ans += nhash[ hash(-num) ].tot;
}
}else
{
for( int i = 1 ; i <= m ; i++ )
{
dfs( c+1, num+f[i][c] ,p);
}
}
}
int main()
{
while(scanf("%d %d",&n,&m)==2)
{
ans = 0;
for(int i = 0 ; i < n ; i ++ )
{
scanf("%d %d",k+i,p+i);
}
for(int i = 1 ; i <= m ; i ++ )
{
for( int j = 0 ; j < n ; j ++ )
{
f[ i ][ j ] = k[ j ] * quickPower( i , p[ j ] );
}
}
if( n==1 )
{
if( k[0] == 0 )
{
printf("%d\n",m);
}else puts("0");
continue;
}
memset(nhash,0,sizeof(nhash));
dfs(0,0,n>>1);
dfs(n>>1,0,n);
printf("%d\n",ans);
}
return 0;
}