凑方程解

来源:互联网 发布:java 建造模式 编辑:程序博客网 时间:2024/05/26 02:56

题目描述
小明正在为一个化学方程式解一个四元一次方程,已知A*x1+B*x2+C*x3+D*x4=P,其中x1,x2,x3,x4是未知数,A,B,C,D是已知的系数。可是这个方程的解实在是太多了,经过一系列运算,小明确定了4个未知数只可能从一个包含了N个正整数的S[]数组中产生,每个未知数都可以从S[1]到S[N]中选择,不同未知数选的值可以相同。保证S数组中所有的数字都不相同。
现在已知A,B,C,D,P,N,以及N个整数,问共有多少组方程的解。

输入
第一行6个整数A,B,C,D,P,N,分别表示方程的系数和S数组中的元素个数。
第二行N个正整数,表示S数组中的元素。

输出
一个整数,表示方程的解的数量。(未知数的值只能从S数组中产生,其他的解即使符合条件也不计入答案中)。

样例输入
1 1 1 1 4 4
1 2 3 4
样例输出
1

提示
【样例解释】
当x1=x2=x3=x4=1时,等式成立。
【数据范围】
对于40%的数据,N<=50。
对于70%的数据,N<=200。其中系数的绝对值|A|,|B|,|C|,|D|均小于500,|P|<=10^6,S数组中每个元素|s[i]|<=500
对于100%的数据,N<=1000,其中系数的绝对值|A|,|B|,|C|,|D|均小于10000,|P|<=4*10^13,S数组中每个元素|s[i]|<=10^9。

Solution

b数组记录A*s[i]+B*s[j]
c数组记录C*s[i]+D*s[j]
枚举b,在c里二分找p-b[i]
但是这样还是有点慢,我们可以加个小优化(从单调性考虑,可以先自己思考一下)

#include<cstdio>#include<cstring>#include<iostream>#include<algorithm>#define ll long longusing namespace std;ll A,B,C,D,p,w,ans;ll b[1000005],c[1000005];int n,len,up,down;int a[1005];int erfen1(int l,int r,ll x) //二分找上界{    if(l>r) return r;    int mid=(l+r)/2;    if(x>=c[mid]) return erfen1(mid+1,r,x); else return erfen1(l,mid-1,x);}int erfen2(int l,int r,ll x) //二分找下界{    if(l>r) return l;    int mid=(l+r)/2;    if(x<=c[mid]) return erfen2(l,mid-1,x); else return erfen2(mid+1,r,x); }int main(){    cin>>A>>B>>C>>D>>p>>n;    for(int i=1;i<=n;i++) scanf("%d",&a[i]);    for(int i=1;i<=n;i++)     for(int j=1;j<=n;j++)     {        len++;        b[len]=A*a[i]+B*a[j];        c[len]=C*a[i]+D*a[j];    }    sort(b+1,b+len+1);    sort(c+1,c+len+1);    w=len;    int i=1;    while(1)     {        up=erfen1(1,w,p-b[i]);        down=erfen2(1,w,p-b[i]);        if(c[up]==p-b[i]&&c[down]==p-b[i]) ans=ans+up-down+1;        while(i<len&&b[i+1]==b[i]) //后面一样直接用当前结果        {            i++;            ans=ans+up-down+1;        }        i++;        if(i>len) break;         w=down-1; //缩小二分范围    }    cout<<ans;    return 0;}
0 0