把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

qzezoj 1547 疫病控制

题面传送门
话说这道题题面和题目一点儿不沾边啊\(QAQ\),而且还有错别字:大米有\(32768\)棵(颗)……
扯了半天,题目要求解一个方程组:\(ax1+bx2+cx3+dx4=0\),\(0<=x1,x2,x3,x4<=m\)
第一想法:枚举:四重循环,枚举\(x1\),\(x2\),\(x3\),\(x4\),复杂度\(O(n^4)\)
代码谁都会打(只不过没打而已)
第二想法:构造:三重循环,枚举\(x1\),\(x2\),\(x3\),把\(x4\)解出来,若满足不等式,则方案数\(++\)
代码实现:

#include<cstdio>
using namespace std;
int a,b,c,d,m,ans,pus,now;
int main(){
    register int i,j,k;
    scanf("%d%d%d%d%d",&a,&b,&c,&d,&m);
    if(a>0&&b>0&&c>0&&d>0){printf("1");return 0;}
    for(i=0;i<=m;i++){
        for(j=0;j<=m;j++){
            for(k=0;k<=m;k++){
                if((i*a+j*b+k*c)%d==0&&(i*a+j*b+k*c)/d*-1>=0&&(i*a+j*b+k*c)/d*-1<=m) ans++;
            }
        }
    }
    printf("%d",ans);
}

第三想法:数论优化:两重循环,枚举\(x1\),\(x2\),求出\(x3\)\(x4\)的最小公倍数,并枚举出第一种解,然后每次循环加上最小公倍数,可以理解成用\(\frac{x4}{gcd(x3,x4)}\)\(x3\)替换\(\frac{x3}{gcd(x3,x4)}\)\(x4\).
代码实现(不知道为什么,比不优化\(T\)的还多):

#include<cstdio>
#include<algorithm>
using namespace std;
int a,b,c,d,m,ans,pus,now,lcm;
int main() {
    register int i,j,k;
    scanf("%d%d%d%d%d",&a,&b,&c,&d,&m);
    lcm=abs(c*d/__gcd(c,d));
    if(a>0&&b>0&&c>0&&d>0) {
        printf("1");
        return 0;
    }
    for(i=0; i<=m; i++) {
        for(j=0; j<=m; j++) {
            if(!c) {
                if((-1*i*a-b*j)%d==0) ans+=m+1;
            } else if(!d) {
                if((-1*i*a-b*j)%c==0) ans+=m+1;
            } else {
                now=1;
                for(k=0; k<=m&&k>=0; k+=now) {
                    if((i*a+j*b+k*c)%d==0&&(i*a+j*b+k*c)/d*-1>=0&&(i*a+j*b+k*c)/d*-1<=m) {ans++;now=lcm;}
                }
            }
        }
    }
    printf("%d",ans);
}

第四想法:数形结合。利用平面直角坐标系,枚举\(x1\)\(x2\)后,枚举两个点,然后确定一条直线,在这条直线的\(0\)\(m\)之间找整点。代码尚未成功,同志还需努力
第五想法:折半搜索,枚举一半,用\(map\)记录,再枚举一半,在\(map\)中查找是否有点,加入答案。两重循环。
代码实现(也不知道怎么回事,比三重循环\(T\)得多):

#include<cstdio>
#include<map>
using namespace std;
int a,b,c,d,m,ans;
map<int,int> fz,ff;
int main(){
    register int i,j;
    scanf("%d%d%d%d%d",&a,&b,&c,&d,&m);
    for(i=0;i<=m;i++){
        for(j=0;j<=m;j++){
            if(a*i+j*b<0) ff[-1*a*i+j*b]++;
            else fz[a*i+j*b]++;
        }
    }
    for(i=0;i<=m;i++){
        for(j=0;j<=m;j++) {
            if((c*i+d*j)>0) ans+=ff[c*i+d*j];
            else ans+=fz[(c*i+d*j)*-1];
        }
    }
    printf("%d",ans);
}

第六想法:同第五想法一样,只不过用了\(unordered\_map\),本质是哈希
第七想法:手写哈希,这里用开放寻址法,其实挂链表法和邻接表差不多,但更麻烦。开放寻址法的主旨是:如果位置被占,就往后移。
代码实现:

#include<cstdio>
#include<map>
using namespace std;
int a,b,c,d,m,ans,f1[5000039],f2[5000039],g1[5000039],g2[5000039];
inline void get1(int x){
    int tmp=x%5000007;
    while(f1[tmp]!=x){
        if(!g1[tmp]) break;
        tmp=(tmp+1)%5000007;
    }
    f1[tmp]=x;
    g1[tmp]++;
}
inline void get2(int x){
    int tmp=x%5000007;
    while(f2[tmp]!=x){
        if(!g2[tmp]) break;
        tmp=(tmp+1)%5000007;
    }
    f2[tmp]=x;
    g2[tmp]++;
}
inline int find1(int x){
    int tmp=x%5000007;
    while(f1[tmp]!=x){
        if(!g1[tmp]) break;
        tmp=(tmp+1)%5000007;
    }
    if(!g1[tmp]) return 0;
    return g1[tmp];
}
inline int find2(int x){
    int tmp=x%5000007;
    while(f2[tmp]!=x){
        if(!g2[tmp]) break;
        tmp=(tmp+1)%5000007;
    }
    if(!g2[tmp]) return 0;
    return g2[tmp];
}
int main(){
    register int i,j,now;
    scanf("%d%d%d%d%d",&a,&b,&c,&d,&m);
    for(i=0;i<=m;i++){
        for(j=0;j<=m;j++){
            now=i*a+j*b;
            if(now>0) get1(now);
            else get2(-now);
        }
    }
    for(i=0;i<=m;i++){
        for(j=0;j<=m;j++) {
            now=c*i+d*j;
            if(now>=0) ans+=find2(now);
            else ans+=find1(-now);
            //printf("%d\n",find2(now));
        }
    }
    printf("%d",ans);
}
posted @ 2020-03-26 12:32  275307894a  阅读(46)  评论(0编辑  收藏  举报
浏览器标题切换
浏览器标题切换end