qzezoj 1547 疫病控制

题面传送门
话说这道题题面和题目一点儿不沾边啊 Q A Q QAQ QAQ,而且还有错别字:大米有 32768 32768 32768棵(颗)……
扯了半天,题目要求解一个方程组: a x 1 + b x 2 + c x 3 + d x 4 = 0 ax1+bx2+cx3+dx4=0 ax1+bx2+cx3+dx4=0, 0 < = x 1 , x 2 , x 3 , x 4 < = m 0<=x1,x2,x3,x4<=m 0<=x1,x2,x3,x4<=m
第一想法:枚举:四重循环,枚举 x 1 x1 x1, x 2 x2 x2, x 3 x3 x3, x 4 x4 x4,复杂度 O ( n 4 ) O(n^4) O(n4)
代码谁都会打(只不过没打而已)
第二想法:构造:三重循环,枚举 x 1 x1 x1, x 2 x2 x2, x 3 x3 x3,把 x 4 x4 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);
}

第三想法:数论优化:两重循环,枚举 x 1 x1 x1, x 2 x2 x2,求出 x 3 x3 x3 x 4 x4 x4的最小公倍数,并枚举出第一种解,然后每次循环加上最小公倍数,可以理解成用 x 4 g c d ( x 3 , x 4 ) \frac{x4}{gcd(x3,x4)} gcd(x3,x4)x4 x 3 x3 x3替换 x 3 g c d ( x 3 , x 4 ) \frac{x3}{gcd(x3,x4)} gcd(x3,x4)x3 x 4 x4 x4.
代码实现(不知道为什么,比不优化 T T 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);
}

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

第六想法:同第五想法一样,只不过用了 u n o r d e r e d _ m a p unordered\_map 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);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值