• 数学相关训练题解


    训练链接

    CF645F

    题目链接

    点击打开链接

    题目解法

    一眼莫反
    推式子的步骤就不写了,反正也比较套路
    这里只给出最后的式子为: A n s = ∑ i = 1 V ϕ ( i ) ∗ ( ∑ i ∣ j c n t j k ) Ans=\sum\limits_{i=1}^{V}\phi(i)*\binom{\sum\limits_{i\mid j}cnt_j}{k} Ans=i=1Vϕ(i)(kijcntj),其中 c n t j cnt_j cntj 为数字 j j j 的出现次数
    修改操作的话,根据式子,不难得出只要修改 x x x 的因子即可
    时间复杂度 O ( q n ) O(q\sqrt n) O(qn )

    #include 
    using namespace std;
    const int V=1000000,N=200100,P=1e9+7;
    int n,k,q,a[N];
    int inv[V+100],fac[V+100];
    int mu[V+100],pr[V+100],tot,v[V+100];
    int smu[V+100],mi[V+100],sum[V+100],cnt[V+100];
    vector<int> fact[V+100];
    inline int read(){
    	int FF=0,RR=1;
    	char ch=getchar();
    	for(;!isdigit(ch);ch=getchar()) if(ch=='-') RR=-1;
    	for(;isdigit(ch);ch=getchar()) FF=(FF<<1)+(FF<<3)+ch-48;
    	return FF*RR;
    }
    int qmi(int a,int b){
    	int res=1;
    	for(;b;b>>=1){
    		if(b&1) res=1ll*res*a%P;
    		a=1ll*a*a%P;
    	}
    	return res;
    }
    int C(int a,int b){
        if(a<b) return 0;
        return 1ll*fac[a]*inv[b]%P*inv[a-b]%P;
    }
    int main(){
    	mu[1]=1;
    	for(int i=2;i<=V;i++){
    		if(!v[i]) v[i]=i,pr[++tot]=i,mu[i]=-1;
    		for(int j=1;j<=tot&&pr[j]<=V/i;j++){
    			v[i*pr[j]]=pr[j];
    			if(v[i]==pr[j]) break;
    			mu[i*pr[j]]=-mu[i];
    		}
    	}
        fac[0]=1;
        for(int i=1;i<=V;i++) fac[i]=1ll*fac[i-1]*i%P;
        inv[V]=qmi(fac[V],P-2);
        for(int i=V-1;i>=0;i--) inv[i]=1ll*inv[i+1]*(i+1)%P;
    	n=read(),k=read(),q=read();
    	for(int i=1;i<=n+q;i++) mi[i]=C(i,k);
    	for(int i=1;i<=n;i++) a[i]=read(),cnt[a[i]]++;
    	for(int i=1;i<=V;i++) for(int j=i;j<=V;j+=i) sum[i]+=cnt[j],smu[j]=((smu[j]+mu[i]*(j/i))%P+P)%P,fact[j].push_back(i);
    	int ans=0;
    	for(int i=1;i<=V;i++) ans=((ans+1ll*smu[i]*mi[sum[i]])%P+P)%P;
    	for(int i=1;i<=q;i++){
    		int x=read();
    		for(int d:fact[x]){
    			ans=((ans-1ll*smu[d]*mi[sum[d]])%P+P)%P;
    			sum[d]++;
    			ans=((ans+1ll*smu[d]*mi[sum[d]])%P+P)%P;
    		}
    		printf("%d\n",ans);
    	}
    	fprintf(stderr,"%d ms\n",int(1e3*clock()/CLOCKS_PER_SEC));
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59

    CF1034C

    题目链接

    点击打开链接

    题目解法

    翻译有误!!!
    显然我们的问题是要求出这棵树分成 k k k 个部分是否可行,如果可行,令 f k = 1 f_k=1 fk=1
    那么只要做一遍 d p dp dp,即可,式子为 f i = ∑ i ∣ j f j f_i=\sum\limits_{i|j}f_j fi=ijfj,注意 i , j i,j i,j 一定要是可行的

    考虑如何求 f k f_k fk
    我们先求一遍子树和,令权值总和为 S S S,那么需要割的边即为满足 S k ∣ s i \frac{S}{k}|s_i kSsi 的子树和
    我们需要判断需要割的边的个数是否 = k =k =k
    考虑如何快速求解上面的问题
    s i = a S k s_i=a\frac{S}{k} si=akS
    k = a S s i k=a\frac{S}{s_i} k=asiS
    所以说如果 k k k S ( S , s i ) \frac{S}{(S,s_i)} (S,si)S 的倍数,那么 i i i 就会对 k k k 产生贡献,直接扫一遍 S ( S , s i ) \frac{S}{(S,s_i)} (S,si)S n n n 以内的倍数即可
    时间复杂度 O ( n ln ⁡ n ) O(n\ln n) O(nlnn)

    #include 
    #define int long long
    using namespace std;
    const int N=1000100,P=1e9+7;
    int n,v[N],fa[N],s[N],f[N];
    int cnt[N];
    inline int read(){
    	int FF=0,RR=1;
    	char ch=getchar();
    	for(;!isdigit(ch);ch=getchar()) if(ch=='-') RR=-1;
    	for(;isdigit(ch);ch=getchar()) FF=(FF<<1)+(FF<<3)+ch-48;
    	return FF*RR;
    }
    signed main(){
    	n=read();
    	for(int i=1;i<=n;i++) v[i]=read();
    	for(int i=2;i<=n;i++) fa[i]=read();
    	for(int i=n;i>=1;i--) s[i]+=v[i],s[fa[i]]+=s[i];
    	int tot=s[1];
        for(int i=1;i<=n;i++){
            int g=tot/__gcd(tot,s[i]);
            for(int j=g;j<=n;j+=g) cnt[j]++;
        }
    	for(int i=1;i<=n;i++) if(i==cnt[i]) f[i]=1;
    	for(int i=n;i>=2;i--){
    		if(!f[i]) continue;
    		for(int j=i+i;j<=n;j+=i) f[i]=(f[i]+f[j])%P;
    	}
    	int ans=1;
    	for(int i=2;i<=n;i++) ans=(ans+f[i])%P;
    	printf("%lld\n",ans);
    	fprintf(stderr,"%d ms\n",int64_t(1e3*clock()/CLOCKS_PER_SEC));
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    CF1603D

    题目链接

    点击打开链接

    题目解法

    首先可以发现当 k ≤ l o g 2 n k\le log_2^n klog2n 时,答案一定为 n n n,因为可以按照二进制的位数分组,然后每组的 c c c 都为 1 1 1
    现在只需要考虑 k ≤ 16 k\le 16 k16 的情况
    我们令 f i , j f_{i,j} fi,j 为到 j j j 分成 i i i 段的最小值
    看题解发现 f i , j f_{i,j} fi,j 对于每一层的 i i i 满足决策单调性,所以可以直接分治解决
    现在的问题是快速算出 c ( l , r ) c(l,r) c(l,r)
    推式子 . . . ... ...
    自己在草稿纸上推可以得出 c ( l , r ) = ∑ i = l r ∑ j = 1 ⌊ r i ⌋ ϕ ( j ) c(l,r)=\sum\limits_{i=l}^r\sum\limits_{j=1}^{\lfloor\frac{r}{i}\rfloor}\phi(j) c(l,r)=i=lrj=1irϕ(j)
    发现这很分块
    具体来说的话可以算出 ϕ \phi ϕ 的前缀和,然后分类讨论,和累加
    感觉是道挺妙的题,但不太好讲,自己去看题解吧,讲的挺详细的
    时间复杂度 O ( 16 n l o g n ) O(16nlogn) O(16nlogn)

    #include 
    #define int long long
    using namespace std;
    const int K=20,MAXB=330,N=100100,inf=1e18;
    int cur,phi[N],s[N],f[K][N];
    int B=320,g[N][MAXB],h[N][MAXB];
    inline int read(){
    	int FF=0,RR=1;
    	char ch=getchar();
    	for(;!isdigit(ch);ch=getchar()) if(ch=='-') RR=-1;
    	for(;isdigit(ch);ch=getchar()) FF=(FF<<1)+(FF<<3)+ch-48;
    	return FF*RR;
    }
    int calc_bl(int l,int r){
        int res=0;
        for(int i=l;i<=r;i++) res+=s[r/i];
        return res;
    }
    int calc(int l,int r){
    	if(r<=B) return g[r][r]-g[r][l-1];
    	else{
    		int res=0;
    		if(l<=B) res=g[r][B]-g[r][l-1];
    		l=max(l,B+1);
    		int L=l,R=r/(r/l);
    		res+=h[r][r/l-1]+(R-L+1)*s[r/l];
    		return res;
    	}
    }
    void solve(int l,int r,int ql,int qr){
    	if(l>r) return;
    	int mid=(l+r)>>1;
    	int res=inf,pos;
    	for(int i=ql;i<=min(mid-1,qr);i++)
            if(f[cur-1][i]!=inf&&f[cur-1][i]+calc(i+1,mid)<res)
    			res=f[cur-1][i]+calc(i+1,mid),pos=i;
    	f[cur][mid]=res;
    	solve(l,mid-1,ql,pos),solve(mid+1,r,pos,qr);
    }
    void work(){
    	int n=read(),k=read();
    	if(k>16||n<(1<<k)) printf("%d\n",n);
    	else printf("%lld\n",f[k][n]);
    }
    signed main(){
        for(int i=1;i<N;i++){
            phi[i]=i;int t=i;
            for(int j=2;j*j<=t;j++) if(t%j==0){
                while(t%j==0) t/=j;
                phi[i]=phi[i]/j*(j-1);
            }
            if(t>1) phi[i]=phi[i]/t*(t-1);
        }
        for(int i=1;i<N;i++) s[i]=s[i-1]+phi[i];
    	for(int i=1;i<N;i++) for(int j=1;j<=B;j++) g[i][j]=g[i][j-1]+s[i/j];
    	for(int i=1;i<N;i++) for(int j=1;j<=B;j++){
    		int r=i/j,l=i/(j+1)+1;
    		h[i][j]+=(r-l+1)*s[j];
    	}
    	for(int i=1;i<N;i++) for(int j=1;j<=B;j++) h[i][j]+=h[i][j-1];
        memset(f,0x3f,sizeof(f));f[0][0]=0;
        for(int i=1;i<=16;i++) cur=i,solve(1,N-1,0,N-1);
    	int T=read();
    	while(T--) work();
    	fprintf(stderr,"%d ms\n",int64_t(1e3*clock()/CLOCKS_PER_SEC));
    	return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68

    CF1770F

    题目链接

    点击打开链接
    感觉是一道很妙的题!!!反正我是完全没思路

    考虑题目中的和限制与或限制有很好的对称性,那么不难发现 a 1 a_1 a1 的位置可以换到其他任何位置上,那么当 n n n 为偶数的时候答案即为 0 0 0,当 n n n 为奇数的时候答案为所有情况下 a 1 a_1 a1 的异或和(因为异或可以抵消,且 a 1 a_1 a1 可以换到任何一个位置,那么当 n n n 为奇数时一定只会剩下一个位置未被抵消)

    首先一个朴素的想法是对 a 1 a_1 a1 分位计算,那么现在我们需要求当 2 i ⊆ a 1 2^i\subseteq a_1 2ia1 的满足条件的方案数的奇偶性
    对于 或 操作,具有比较好的性质(只有可能从 0 0 0 变为 1 1 1),所以我们考虑容斥求解
    f y ′ ( y ′ ⊆ y ) f_{y'}(y'\subseteq y) fy(yy) ∀ a i ⊆ y ′ \forall a_i\subseteq y' aiy ∑ a i = x \sum a_i=x ai=x 的方案数
    所以方案数即为 ∑ y ′ ⊆ y ( − 1 ) ∣ y ∣ − ∣ y ′ ∣ f y ′ \sum\limits_{y'\subseteq y}(-1)^{|y|-|y'|}f_{y'} yy(1)yyfy
    因为我们只关心奇偶性,所以方案数可化简为 ∑ y ′ ⊆ y f y ′ \sum\limits_{y'\subseteq y}f_{y'} yyfy
    需要满足 a i ⊆ y ′ a_i\subseteq y' aiy 的条件不好办,所以我们考虑组合数逆用,可得 a i ⊆ y ′    ⟺    ( y ′ a i ) ( m o d    2 ) a_i\subseteq y'\iff \binom{y'}{a_i}(\mod 2) aiy(aiy)(mod2)
    f y ′ f_{y'} fy 拆分开来即为 ∑ ∑ a i = x − 2 i ( y − 2 i a 1 ) ( y a 2 ) . . . ( y a n ) \sum\limits_{\sum a_i=x-2^i}\binom{y-2^i}{a_1}\binom{y}{a_2}...\binom{y}{a_n} ai=x2i(a1y2i)(a2y)...(any)
    用范德蒙德卷积化简可得
    ( n y − 2 i x − 2 i ) ( m o d    2 ) = [ ( x − 2 i ) ⊆ ( n y − 2 i ) ] \binom{ny-2^i}{x-2^i}(\mod 2)\\=[(x-2^i)\subseteq(ny-2^i)] (x2iny2i)(mod2)=[(x2i)(ny2i)]
    直接计算即可
    时间复杂度 O ( y l o g y ) O(ylogy) O(ylogy)

    #include 
    #define int long long
    using namespace std;
    int n,x,y;
    inline int read(){
    	int FF=0,RR=1;
    	char ch=getchar();
    	for(;!isdigit(ch);ch=getchar()) if(ch=='-') RR=-1;
    	for(;isdigit(ch);ch=getchar()) FF=(FF<<1)+(FF<<3)+ch-48;
    	return FF*RR;
    }
    signed main(){
    	n=read(),x=read(),y=read();
    	if(n&1){
    		int ans=0;
    		for(int i=0;i<20;i++)
    			if(y>>i&1)
    				for(int yy=y;;yy=(yy-1)&y){
    					if(yy>>i&1){
    						int p=n*yy-(1<<i),q=x-(1<<i);
    						if(p>=0&&q>=0&&(p&q)==q) ans^=1<<i;
    					}
    					if(!yy) break;
    				}
    		printf("%lld\n",ans);
    	}
    	else puts("0");
    	fprintf(stderr,"%d ms\n",int64_t(1e3*clock()/CLOCKS_PER_SEC));
    	return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    CF1656H

    题目链接

    点击打开链接

    题目解法

    还是一道很妙的题!!!可能是我太菜了
    我们一开始把所有数都选入集合中,然后考虑删数使两个集合的 l c m lcm lcm 相等
    这个直接暴力查找删除即可,即找到 a i ∤ ∏ b j a_i\nmid \prod b_j aibj b j b_j bj 是仍在答案集合中的
    一个判断 a i ∤ ∏ b j a_i\nmid \prod b_j aibj 的直观想法是质因数分解,然后看次幂,但是 a i , b i ≤ 4 ∗ 1 0 36 a_i,b_i\le 4*10^{36} ai,bi41036,明显不够做任何的质因数分解算法( p o l l a r d − r h o pollard-rho pollardrho 也跑不了)
    所以考虑换一个角度思考问题
    这里有一个很妙的判断方法是 gcd ⁡ ( a gcd ⁡ ( a , b 1 ) , a gcd ⁡ ( a , b 2 ) , . . . , a gcd ⁡ ( a , b k ) ) \gcd(\frac{a}{\gcd(a,b_1)},\frac{a}{\gcd(a,b_2)},...,\frac{a}{\gcd(a,b_k)}) gcd(gcd(a,b1)a,gcd(a,b2)a,...,gcd(a,bk)a) k k k 为仍在答案集合中的数的个数,上面这个式子分类讨论然后自己分析一下不难理解
    因为 gcd ⁡ \gcd gcd 是可合并的,且需要支持修改,所以考虑用线段树维护
    考虑每次删除数只会修改另一个序列中的当前位置,所以线段树只会修改 O ( n 2 ) O(n^2) O(n2) 次,删除数的话直接把 gcd ⁡ \gcd gcd 赋为 0 0 0 即可,因为 gcd ⁡ ( x , 0 ) = x \gcd(x,0)=x gcd(x,0)=x
    时间复杂度为 O ( n 2 l o g n l o g V ) O(n^2lognlogV) O(n2lognlogV)

    #include 
    using namespace std;
    const int N=1100;
    typedef __int128_t LL;
    LL a[N],b[N];
    set<int> A,B;
    inline LL read(){
    	LL FF=0,RR=1;
    	char ch=getchar();
    	for(;!isdigit(ch);ch=getchar()) if(ch=='-') RR=-1;
    	for(;isdigit(ch);ch=getchar()) FF=FF*10+ch-48;
    	return FF*RR;
    }
    LL gcd(LL x,LL y){ return !y?x:gcd(y,x%y);}
    void print(LL x){
    	if(!x) return;
    	print(x/10);
    	putchar(x%10+48);
    }
    struct SegmentTree{
    	LL seg[N<<2];
    	void modify(int l,int r,int x,int pos,LL v){
    		if(l==r){ seg[x]=v;return;}
    		int mid=(l+r)>>1;
    		if(mid>=pos) modify(l,mid,x<<1,pos,v);
    		else modify(mid+1,r,x<<1^1,pos,v);
    		seg[x]=gcd(seg[x<<1],seg[x<<1^1]);
    	}
    	LL ask(){ return seg[1];}
    }sg[N<<1];
    void work(){
    	int n,m;scanf("%d%d",&n,&m);
    	for(int i=1;i<=n;i++) a[i]=read();
    	for(int i=1;i<=m;i++) b[i]=read();
    	for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) sg[i].modify(1,m,1,j,a[i]/gcd(a[i],b[j]));
    	for(int i=1;i<=m;i++) for(int j=1;j<=n;j++) sg[i+n].modify(1,n,1,j,b[i]/gcd(b[i],a[j]));
    	int lenA=n,lenB=m;
        A.clear(),B.clear();
    	for(int i=1;i<=n;i++) A.insert(i);
    	for(int i=1;i<=m;i++) B.insert(i);
    	while(!A.empty()&&!B.empty()){
    		set<int> del;del.clear();
    		for(auto it:A)
    			if(sg[it].ask()>1){
    				del.insert(it);lenA--;
    				for(auto itt:B) sg[n+itt].modify(1,n,1,it,0);
    			}
    		for(auto it:del) A.erase(it);
    		bool flg=(del.size()>0);
    		del.clear();
    		for(auto it:B)
    			if(sg[n+it].ask()>1){
    				del.insert(it);lenB--;
    				for(auto itt:A) sg[itt].modify(1,m,1,it,0);
    			} 
    		for(auto it:del) B.erase(it);
    		flg|=(del.size()>0);
    		del.clear();
    		if(!flg) break;
    	}
    	if(A.empty()||B.empty()) puts("NO");
    	else{
    		puts("YES");
    		print(lenA);putchar(' ');print(lenB);putchar('\n');
    		for(auto it:A) print(a[it]),putchar(' ');puts("");
    		for(auto it:B) print(b[it]),putchar(' ');puts("");
    	}
    }
    int main(){
    	int T;scanf("%d",&T);
    	while(T--) work();
    	fprintf(stderr,"%d ms\n",int(1e3*clock()/CLOCKS_PER_SEC));
    	return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75

    CF986F

    题目链接

    点击打开链接

    题目解法

    挺有意思的一道题(感觉十分多合一)
    首先考虑到只有 k k k 的质因子是有用的,因为合数因子一定可以表示成多个质因子相加
    那么现在的问题变成了询问 n n n 是否是 p 1 , . . . , p m p_1,...,p_m p1,...,pm 的线性组合,且每一项的系数需要非负
    线性组合且非负,我们第一个想到的问题应该是 P3403 跳楼机,所以这道题我们可以套用那道题的做法,用同余最短路做
    但我们发现 p i p_i pi 很大,最小的 p i p_i pi 甚至都不能做最短路,所以我们考虑分类讨论:

    1. m = 1 m=1 m=1,直接判断 n n n 是否是 p p p 的倍数即可
    2. m = 2 m=2 m=2,问题是需要判断是否存在 d 1 x + d 2 y = n d_1x+d_2y=n d1x+d2y=n 的非负整数解
      如果 ( d 1 , d 2 ) ∤ n (d_1,d_2)\nmid n (d1,d2)n,则无解,考虑 ( d 1 , d 2 ) ∣ n (d_1,d_2)\mid n (d1,d2)n 的情况
      问题等价于 d 2 y ≡ n ( m o d    d 1 ) d_2y\equiv n (\mod d_1) d2yn(modd1) 的最小整数解 y y y 是否满足 y d 2 ≤ n yd_2\le n yd2n
      y y y 的最小整数解显然为 n × d 2 − 1 n\times d_2^{-1} n×d21
      直接求逆元即可
    3. m ≥ 3 m\ge 3 m3,这类情况我们直接套用同余最短路的做法,因为 ∏ p i ≤ 1 0 15 \prod p_i\le 10^{15} pi1015,且 m ≥ 3 m\ge 3 m3,所以最小的 p i ≤ 1 0 5 p_i\le 10^5 pi105,可以直接做最短路

    我是直接暴力质因数分解的(当然要先筛出质数),也可以过
    因为是多合一做法,所以时间复杂度需要一个一个单独算,我就不算了,反正可以过

    #include 
    using namespace std;
    typedef long long LL;
    const int LIM=31700000;
    const int MAXT=10100;
    const LL inf=2e18;
    struct QUERY{ LL n,k;int id;}q[MAXT];
    LL n,k;
    int pr[LIM+5],cnt;
    bool del[LIM+5],ans[MAXT];
    vector<LL> v;
    void sieve(){
    	for(int i=2;i<=LIM;i++){
    		if(!del[i]) pr[++cnt]=i;
    		for(int j=1;j<=cnt&&i<=LIM/pr[j];j++){
    			del[pr[j]*i]=1;
    			if(i%pr[j]==0) break;
    		}
    	}
    }
    void sep(LL x){
    	for(int i=1;i<=cnt&&1ll*pr[i]*pr[i]<=x;i++)
    		if(x%pr[i]==0){
    			v.push_back(pr[i]);
    			while(x%pr[i]==0) x/=pr[i];
    		}
    	if(x>1) v.push_back(x);
    }
    LL Gcd(LL x,LL y){
    	if(!y) return x;
    	return Gcd(y,x%y);
    }
    const int MNPR=100100;
    typedef pair<LL,int> pli;
    priority_queue<pli,vector<pli>,greater<pli> > que;
    int h[MNPR],ne[MNPR*20],e[MNPR*20],idx;
    LL dis[MNPR],w[MNPR*20];
    bool vis[MNPR];
    void add(int x,int y,LL z){ e[idx]=y,w[idx]=z,ne[idx]=h[x],h[x]=idx++;}
    void dij(){
    	dis[0]=0,que.push(make_pair(0,0));
    	while(!que.empty()){
    		int u=que.top().second;que.pop();
    		for(int i=h[u];~i;i=ne[i]){
    			int v=e[i];
    			if(dis[u]+w[i]<dis[v]) dis[v]=dis[u]+w[i],que.push(make_pair(dis[v],v));
    		}
    	}
    }
    int qmi(LL a,int b,int P){
        a%=P;
        int res=1;
        for(;b;b>>=1){
            if(b&1) res=1ll*res*a%P;
            a=a*a%P;
        }
        return res;
    }
    int main(){
    	sieve();
    	int T;scanf("%d",&T);
    	for(int i=1;i<=T;i++){
    		LL n,k;scanf("%lld%lld",&n,&k);
    		q[i]={n,k,i};
    	}
    	sort(q+1,q+T+1,[](const QUERY &x,const QUERY &y){ return x.k<y.k;});
    	for(int i=1,j;i<=T;i=j+1){
    		j=i;
    		while(q[j+1].k==q[i].k) j++;
    		if(q[i].k==1) continue;
    		v.clear();sep(q[i].k);
    		if(v.size()==1){
    			LL d=v[0];
    			for(int x=i;x<=j;x++) if(q[x].n%d==0) ans[q[x].id]=1;
    			continue;
    		}
    		if(v.size()==2){
    			LL d1=v[0],d2=v[1];
    			for(int x=i;x<=j;x++){
    				LL t=q[x].n%d1*qmi(d2,d1-2,d1)%d1;
    				if(t*d2<=q[x].n) ans[q[x].id]=1;
    			}
    			continue;
    		}
    		idx=0;
    		for(int x=0;x<v[0];x++) h[x]=-1,dis[x]=inf;
    		for(int x=0;x<v[0];x++) for(int t=1;t<v.size();t++) add(x,(x+v[t])%v[0],v[t]);
    		dij();
    		for(int x=i;x<=j;x++) if(dis[q[x].n%v[0]]<=q[x].n) ans[q[x].id]=1;
    	}
    	for(int i=1;i<=T;i++) puts(ans[i]?"YES":"NO");
    	fprintf(stderr,"%d ms\n",int(1e3*clock()/CLOCKS_PER_SEC));
    	return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
  • 相关阅读:
    把excel文件内容转化为json文件
    go版本1.16.5 运行项目出现undefined: math.MaxInt报错
    spring boot 项目中的application不能执行是什么问题
    解决Kibana初始化失败报错: Unable to connect to Elasticsearch
    # Vue 中 JSON 编辑器使用
    DNS部署与安全
    焕新出发,利尔智达天下
    Python Django 模版全解与实战
    三、ECMAScript 6 语法简介(1)
    什么是内存泄漏,为什么threadlocal会造成内存泄漏?
  • 原文地址:https://blog.csdn.net/djx2021/article/details/133274172