P5330 [SNOI2019] 数论 题解

一种神奇的做法(?)。

模拟赛 exCRT 打错了导致只有 80……

前置:exCRT。以下有些没解释的就是 exCRT 的内容。

首先,因为是 \(0\)\(T-1\),所以以下的 \(T\) 代表的是 \(T-1\)

看到 \(\begin{cases}i\equiv x(\bmod\ P)\\i\equiv y(\bmod\ Q)\end{cases}(0 \le i \le T,x \in A,y \in B)\) 的形式,自然想到 exCRT。

首先,\(0\)\(T\) 能被按照对 \(\operatorname{lcm}(P,Q)\) 取模的结果划分开,取模得到 \(k\) 的数的个数为\(\lfloor\frac{T}{\operatorname{lcm}(P,Q)}\rfloor+[T\mod \operatorname{lcm}(P,Q)\ge k]\)

发现如果 \(k\) 为已知余数分别为 \(x,y\) 得到的最小解,那么对 \(\operatorname{lcm}(P,Q)\) 取模等于 \(k\) 的解都是可以的。

但是可能的 \(x,y\) 对有 \(nm\) 对,需要快速求解。

首先先把无解的区分开,根据 exCRT 可知,\((a-b)\mod \gcd(p,q)(a\in A,b\in B)\) 要等于 \(0\) 才有解,因此需要 \(a\equiv b (\bmod \gcd(p,q))(a\in A,b\in B)\)。可以考虑把 \(A\)\(B\) 集合内的每一个数都分别划分开,然后后面枚举 \(i\)\(0\sim\gcd(p,q)\),每次只看 \(\mod\gcd(p,q)=i\)\(A\)\(B\)(分别设为集合 \(A_i\)\(B_i\))。因为 \(\sum_{i=0}^{\gcd(p,q)-1}|A_i|=|A|\)\(B\) 同理,因此时间复杂度仍为线性的。

接着,求数量的 \(\lfloor\frac{T}{\operatorname{lcm}(P,Q)}\rfloor+[T\mod \operatorname{lcm}(P,Q)\ge k]\) 可以被拆成 \(\lfloor\frac{T}{\operatorname{lcm}(P,Q)}\rfloor\)\([T\mod \operatorname{lcm}(P,Q)\ge k]\)。前半段直接等于 \(\lfloor\frac{T}{\operatorname{lcm}(P,Q)}\rfloor \times |A_i| \times |B_i|\ (0 \le i \le \gcd(p,q)-1)\),后边半段才是关键的。

\(k\) 因为大家的 exCRT 不同,展开也不同,但无论如何都可以化为 \(\text{一坨系数}\times a_{ij}+\text{一坨系数}\times b_{ij} \ (0 \le i \le \gcd(p,q)-1,a_{ij} \in A_i)\),设 \(ta_{ij}\)\(\text{一坨系数}\times a_{ij}\)\(tb_{ij}\) 同理。可以考虑直接定一求一,确定一个 \(b_{ij}\),快速计算有多少个 \(a_{ij}\) 使得 \(0 \le ta_{ij}+tb_{ij} \le T\mod \operatorname{lcm}(P,Q)\),即计算在 \([-tb_{ij} , T\mod \operatorname{lcm}(P,Q)-tb_{ij}]\) 范围内的 \(ta_{ij}\) 个数。(注意因为它是在模 \(\operatorname{lcm}(P,Q)\) 意义下的,所以上面的一切都要对 \(\operatorname{lcm}(P,Q)\) 取模

可以考虑把 \(A_i\)\(B_i\) 排序,直接双指针。总时间复杂度 \(O(n\log n)\)

代码:

#include<bits/stdc++.h>
using namespace std;
namespace estidi{
	vector<long long>a,b,va[1000003],vb[1000003],vva;
	long long xx,yy;
	long long exgcd(long long a,long long b){
		if(!b){
			xx=1;
			yy=0;
			return a;
		}
		long long d=exgcd(b,a%b);
		swap(xx,yy);
		yy-=a/b*xx;
		return d;
	}
	long long inv(long long x,long long p){
		exgcd(x,p);
		return (xx%p+p)%p;
	}
	int main(){
		int p,q,n,m;
		long long x,gc,lc,ck,iv,tt,ans=0;
		scanf("%d%d%d%d%lld",&p,&q,&n,&m,&tt);
		tt--;
		for(int i=1;i<=n;i++){
			scanf("%lld",&x);
			a.push_back(x);
		}
		for(int i=1;i<=m;i++){
			scanf("%lld",&x);
			b.push_back(x);
		}
		gc=__gcd(p,q);
		lc=p/gc*q;
		ck=tt%lc;
		iv=inv(p/gc,q/gc)*p/gc;
		for(int i=0;i<n;i++)
			va[a[i]%gc].push_back(((a[i]-iv*a[i])%lc+lc)%lc);
		for(int i=0;i<m;i++)
			vb[b[i]%gc].push_back(((-iv*b[i])%lc+lc)%lc);
		for(int i=0;i<gc;i++){
			ans+=tt/lc*va[i].size()*vb[i].size();
			sort(va[i].begin(),va[i].end());
			sort(vb[i].begin(),vb[i].end());
			vva=va[i];
//			cerr<<"a:\n";
//			for(long long x:va[i])
//				cerr<<x<<" ";
//			cerr<<"\nb:\n";
//			for(long long x:vb[i])
//				cerr<<x<<" ";
//			cerr<<endl<<ck<<" "<<lc<<" "<<iv<<endl;
			for(long long x:va[i])
				vva.push_back(x+lc);
			int l=-1,r=-1;
			for(long long x:vb[i]){
				long long nl=x,nr=ck+x;
				while(l<(int)vva.size()-1&&vva[l+1]<nl)
					l++;
				while(r<(int)vva.size()-1&&vva[r+1]<=nr)
					r++;
//				cerr<<x<<" "<<l<<" "<<r<<" "<<nl<<" "<<nr<<" "<<endl;
				ans+=r-l;
			}
		}
		printf("%lld",ans);
		return 0;
	}
}
int main(){
	estidi::main();
	return 0;
}
posted @ 2025-02-25 16:49  123asdf123  阅读(18)  评论(0)    收藏  举报