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