hoj 1372 方程的解数

方程的解数

Problem

已知一个n元高次方程:

k 1x 1 p1 + k 2x 2 p2 + ... + k nx n pn = 0

其中: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)均为正整数。
  1. 求一个n元一次方程解的个数(解在一定范围内)枚举(裸搜)
  2. 单向枚举,150^6,无法接受
  3. 双向枚举+hash,150^3*hash的时间复杂度,2s左右过
  4. 双向枚举+二分查找,150^3*log(150^3)...还能接受,12s左右
  5. 双向枚举+排序+挨个对,复杂度同上,10s左右
双向枚举似乎没什么好的优化,(正的放一边,负的放一边?也许会有效果),hash的写法能加速不少首先对于负数,是直接求余呢还是转化为正数再求余,对于这道题,后者比前者能快0.8s~1s其次,求MOD时,多判断一下是否大于MOD再求MOD会比直接求MOD快最后,把hash的添加和查找分开,不做无谓的添加,可以提高速度最最后,内联函数似乎也会快一点

双向枚举+排序+挨个对

#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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值