一道数据结构的水题。。。
题意为给予一个最多含六个未知数的方程k1*x1^p1+k2*x2^p2+k3*x3^p3+k4*x4^p4+k5*x5^p5+k6*x6^p6=0;求其整数解的个数。
其中x1,x2.....x6均大于等于1小于等于M(M为输入的一个正数,最大150最小1)。而且保证过程计算小于2^31。
到这里思路已经蛮清晰了,而且限时为15000MS。。。就是为了暴力过啊 = = (大力出奇迹?)
直接想到的就是暴力DFS,不过150^6也是不好搞的,所以加个优化,根据系数k的正负来分类项,比如选择负系数的项为库,将其所有的和的绝对值存起来,然后再累加正系数项的和,在库中进行查找。【就是相当于把式子变为k1*x1^p1+k2*x2^p2=k3*x3^p3+k4*x4^p4+k5*x5^p5+k6*x6^p6,其中k的值均为正整数,这样做的意义是为了降低时间复杂度,相当于左边为O(M^2),右边为O(M^4)】。这样的话就想到加快查找的效率。。。用字典树存一边的和(二进制形式的)。
=.= 但是这样第一个问题是最坏情况下时间复杂度还是O(M^5),第二个问题是字典树空间要求太大,会超内存(这方法简直够蠢)
所以后来又重新考虑了下,直接选择线性表存,不过不是存一边的和,而是两边的和,而且两边项的数目要平均。
这里要先根据系数的大小进行一次排序,从小到大排。相当于是这样:
【k1*x1^p1+k2*x2^p2+k3*x3^p3=k4*x4^p4+k5*x5^p5+k6*x6^p6,其中k的值不一定都为正整数,但一定有序】
这样的话左边的项中一定含原来是负系数的项,在累加项的过程中,如果发现和小于0则不存入(一个小优化,因为项数是根据系数从小到大排的,所以如果左项的和已经是负数了,说明在左边已经含有原来是正系数的项,那么右边项肯定都是正数项,其和也是正数,无解了)。
然后对两个线性表排下序,时间复杂度O(C*log2C),C=M^3;时间复杂度过得去。然后同时遍历两个线性表,就可以求解了。
#include <stdio.h>
#include <set>
#include <algorithm>
using namespace std;
typedef long long ll;
int const Size = 4100000;
int const MAXM = 160;
int const MAXN = 31;
int Big;
int Ans;
int Zero;
int Num,M;
int Mat[MAXM][MAXN];
int Start[2],End[2];
int Neg[Size];
int Pos[Size];
struct Q
{
int k,p;
}Data[MAXN];
bool cmp1(Q a,Q b)
{
return a.k<b.k;
}
bool cmp2(Q a,Q b)
{
return a.k>b.k;
}
void Pre()
{
Big=0;
Zero=1;
Ans=0;
Neg[0]=Pos[0]=0;
int Limit=1<<30;
ll now;
for(int i=1;i<MAXN;i++)Mat[1][i]=1;
for(int i=2,t;i<MAXM;i++)
{
now=i;t=1;
while(now<Limit)
{
Mat[i][t++]=now;
now*=i;
}
}
}
void Read_Case()
{
scanf("%d %d",&Num,&M);
for(int i=0;i<Num;i++)
{
scanf("%d %d",&Data[i].k,&Data[i].p);
if(!Data[i].k)
{
i--;Num--;
Zero*=M;
}
}
sort(Data,Data+Num,cmp1);
}
void Get_List(int *List,int Left,int Right,int *S,int *E)
{
int start=0,end=1;
int Limit,K,P,ans;
for(int i=Left;i<=Right;i++)
{
Limit=end;
K=Data[i].k;P=Data[i].p;
while(start<Limit)
{
for(int t=1;t<=M;t++)
{
ans=List[start]+K*Mat[t][P];
if(ans>=0)List[end++]=ans;
}
start++;
}
}
*S=start;*E=end;
List[start-1]=-1;
}
void Search()
{
int sum;
int i=Start[0],t=Start[1];
while(i<End[0])
{
if(Pos[i]!=Pos[i-1])
{
sum=0;
while(t<End[1]&&Pos[i]>=Neg[t])
{
if(Neg[t]==Pos[i])sum++;
t++;
}
}
Ans+=sum;
i++;
}
}
void Solve()
{
if(!Num)
{
printf("%d\n",Zero);
}
else if(Data[0].k>0||Data[Num-1].k<0)
{
puts("0");
}
else
{
int Mid=Num/2-1;
for(int i=0;i<=Mid;i++)Data[i].k*=-1;
Get_List(Pos,0,Mid,Start,End);
sort(Data+Mid+1,Data+Num,cmp2);
Get_List(Neg,Mid+1,Num-1,Start+1,End+1);
sort(Pos+Start[0],Pos+End[0]);
sort(Neg+Start[1],Neg+End[1]);
Search();
printf("%d\n",Ans*Zero);
}
}
int main()
{
Pre();
Read_Case();
Solve();
}