T1:喜剧的迷人之处在于
思路:
显然,因为\(a×b\)是完全平方数,所以将\(a\)中的所有完全平方数约了以后的数就是\(b\)的最小值。但是题目还要求\(b\)属于区间\([l,r]\),所以我们可能需要给\(b\)乘上一个或多个完全平方数。然后就做完了。
规则怪谈: 不要闲的没事干莫名其妙地给代码加一个莫名其妙的特判,否则你会\(100→30\)
代码:
$code$
#include<iostream>
#include<cstring>
#define int long long
using namespace std;
const int N=1e6+5;
int T,n,a,l,r,cnt,tot,x,delta,num[N],pr[N],ch[N],la[N],db[N];bool vis[N];
inline void init(){db[1]=1;for(int i=2;i<=1000000;i++){if(!vis[i]) pr[++n]=i;for(int j=i*2;j<=1000000;j+=i) vis[j]=1;//质数 db[i]=i*i;//完全平方数 }
}//预处理
inline void pre(){delta=-1;tot=0;x=1;memset(ch,0,sizeof(ch));memset(num,0,sizeof(num));
}//多测清空
signed main(){ios::sync_with_stdio(false);cin>>T;init();while(T--){pre();cin>>a>>l>>r;for(int i=1;i<=n;i++){if(a%pr[i]==0) ch[++tot]=pr[i];while(a%pr[i]==0) num[tot]++,a/=pr[i];if(a==1) break;}//分解质因数 for(int i=1;i<=tot;i++) if(num[i]%2!=0) x*=ch[i];//最小值 int ll=l/x;for(int i=1;i<=N-5;i++){if(db[i]>=ll){delta=i;break;}}//找到合适的完全平方数 if(db[delta]*x>=l&&db[delta]*x<=r) cout<<x*db[delta]<<'\n';//当前值 else if(db[delta+1]*x>=l&&db[delta+1]*x<=r) cout<<x*db[delta+1]<<'\n';//当前值的下一个值 else cout<<"-1"<<'\n';//那就无解咯 }return 0;
}
T2:镜中的野兽
思路:
显然,\(m=lcm+gcd\)是\(gcd\)的倍数。因此,我们可以枚举\(gcd\)的值\(d\)来确定\(gcd,lca\)的取值,然后给每一个元素除以\(d\),这时的问题转化为:对于一个互质且不重复的序列,其中元素的\(lcm\)为\(\frac{m-d}{d}\),然后对\(lcm\)分解因数,我们不难发现,对于一些元素不含有其中的一部分因数,而有一些元素含有一部分因数的最高次数,所以共有\(2*cnt\)种情况。然后用状压dp来判断某些条件是否达成即可。
代码:
$code$
#include<iostream>
#include<cmath>
#include<cstring>
using namespace std;
const int N=1e5+10;
int n,m,p,cnt,ph[N],a[N],ans,f[N*2][30];//2*cnt个条件,n个元素
inline void pre(int x){int s=sqrt(x),y=x;for(int i=2;i<=s&&i<=y;i++){if(y%i==0){ph[++cnt]=i;a[cnt]=0;while(y%i==0) a[cnt]++,y/=i;}}if(y!=1) ph[++cnt]=y,a[cnt]=1;
}//因数分解
inline void dp(int pos,int st){if(pos==cnt+1){for(int i=(1<<cnt*2)-1;i>=0;i--){for(int j=n-1;j>=0;j--){f[i|st][j+1]=(f[i|st][j+1]+f[i][j])%p;//状压dp }}return ;}//每种因数都处理完了 for(int i=0;i<=a[pos];i++){if(i==0) dp(pos+1,st|(1<<(pos-1)));//不包含该因数 状压一下 else if(i==a[pos]) dp(pos+1,st|(1<<(pos-1+cnt)));//包含全部的该因数 区别一下状压 else dp(pos+1,st);//状态不变 }
}
inline void solve(int x){int lcm=(m-x)/x;if((m-x)<=x) return ;//lcm不可能小于等于1 cnt=0;memset(f,0,sizeof(f));f[0][0]=1;pre(lcm);//分解因数 dp(1,0);//状压dp ans=(ans+f[(1<<(cnt*2))-1][n])%p;//答案加和
}
int main(){ios::sync_with_stdio(false);cin>>n>>m>>p;for(int i=1;i<=sqrt(m);i++){//便利gcd if(m%i==0){solve(i);if(i*i!=m) solve(m/i);//避免重复 }}cout<<ans<<'\n';return 0;
}
T3:我愿相信由你所描述的童话
思路:
遍历没一个数作为分界点的情况,分别dp求左边的方案数与右边的方案数,最后相乘起来就好
代码:
$code$
#include<iostream>
#define int long long
using namespace std;
const int N=3e6+5,mod=1e9+7;
int m,n,ans,a[N],t1[N],t2[N],f1[N],f2[N];
signed main(){ios::sync_with_stdio(false);cin>>n>>m;for(int i=1;i<=n;i++) cin>>a[i];for(int i=1;i<=n;i++){int res=t1[0]+1;for(int j=a[i];j>=1;j=a[i]&(j-1))//去掉末尾的1后的数字 res=(res+t1[j])%mod;//若序列里有,则加上满足它的方案数 f1[i]=res%mod;//左边的 t1[a[i]]=(t1[a[i]]+res)%mod;//更新一下 }for(int i=n;i>=1;i--){//同上 但是由于是从后往前,所以方向相反 int res=t2[0]+1;for(int j=a[i];j>=1;j=a[i]&(j-1))res=(res+t2[j])%mod;f2[i]=(res-t2[a[i]]+mod)%mod;//右边的,减去当前位置与前面任意权值相同位置同时出现的方案数t2[a[i]]=(t2[a[i]]+res)%mod;}for(int i=1;i<=n;i++) ans=(ans+f1[i]*f2[i])%mod;//统计答案~~ cout<<ans<<'\n';return 0;
}
T4:\(Baby ~ Doll\)
不会😛