1 条题解
-
0
题解区怎么是神秘题解?
下面是一篇正常的题解。
Part 1.
你发现你要算 ,先求出 。
如果 不是 的倍数那么答案就是 ,因为约分后 中仍剩有 的因子,也就是 与 不互质(没有逆元)。否则你就把 都除掉 ,然后就可以正常算了。
现在难点在于求出 ,以及如何把 给除掉 。
Part 2.
有一个想法是把 分解质因数,然后对于每个质因子,将其在 中出现次数取 ,就是其在 中的出现次数。
除法的话可以把 也分解质因数,对于每个质因子,将其与 的抵消掉。
最后如果 没有消完,那么就输出 ;否则求出 每个 剩下的质因子之积 ,和 每个 剩下的质因子之积 ,答案即为 ,直接 exgcd 求逆元即可。
现在的问题是
邪恶的出题人并不想让你分解质因数,那咋办?Part 3.
我们引入一个概念:我们也许并不需要分解彻底,只需要分解出“伪质因数”就行。
什么意思呢?举个例子:
对于 来说:
发现我们其实只需要分解到 即可,可以只分解到 是因为质因子 和 要么同时不出现,要么同时出现,我们可以把它们看做一个整体,即“伪质因数”。
对一些数 分解“伪质因数” 需要满足两个性质:
- 两个不同的“伪质因数”互质。
- 每个 都能被表示成 $p _ 1 ^ {c _ 1} p _ 2 ^ {c _ 2} \cdots p _ k ^ {c _ k}$。
现在问题来了,怎么构造“伪质因数”呢?
考虑增量构造,假设之前的“伪质因数”集合为 ,现在加入一个数 :
-
若存在一个数 满足 与 不互质,设 ,则从 中删除 ,并递归加入 、、。
-
否则,向 中直接加入 。
不懂的地方可以看代码,应该写得挺清楚的。
于是我们可以对所有 和 分解“伪质因数”,注意我们只关心 的因数,所以对于一个 实际上加入的是 ,对于 同理,这样“伪质因数”的个数只有 级别。
接下来就可以把“伪质因数”看成普通的质因数,如 Part 2. 所讲来做就行啦。
Part 4.
接下来讲一讲构造复杂度的证明。
对于一次加入操作,查找 是 级别的。
如果找到符合条件的 ,就可能发生分裂 并继续加入 :
-
如果发生了分裂操作即 ,那么 会增大 ,由于最终 的大小只有 ,所以这部分只会带来 次加入操作。
-
接下来会继续递归 ,由于 ,所以每次递归 至少减半,故对于一次原始的加入操作,至多递归加入 次。
所以单次加入复杂度是 ,均摊部分时间复杂度是 。
运用到这题,我们有 次加入操作,并且加入的都是 的因数所以 ,故每次询问复杂度应该是 的。
Part 5.
放一下代码。
#include<cstdio> #include<vector> #include<set> #define N 25 #define M 10005 using namespace std; using ll=long long; using lll=__int128; int n,m,q; ll a[N][M],b[M]; ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;} ll exgcd(ll a,ll b,ll &x,ll &y) { if(!b) return x=1,y=0,a; ll g=exgcd(b,a%b,y,x); y-=a/b*x; return g; } ll inv(ll x,ll p) { // ax=1(mod p) => ax+bp=1 ll a,b; exgcd(x,p,a,b); return (a%p+p)%p; } set<ll> s; void ins(ll x) { if(x==1) return; for(auto u:s) { ll w=gcd(x,u); if(w>1) { if(w<u) { s.erase(u),ins(w),ins(u/w); } return ins(x/w); } } s.insert(x); } int main() { scanf("%d%d%d",&n,&m,&q); for(int i=1;i<=m;i++) scanf("%lld",&b[i]); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) scanf("%lld",&a[i][j]); for(int _=1,i;_<=q;_++) { ll mod; scanf("%d%lld",&i,&mod); s.clear(),s.insert(mod); for(int j=1;j<=m;j++) ins(gcd(a[i][j],mod)),ins(gcd(b[j],mod)); vector<ll> p; for(auto u:s) p.push_back(u); int k=p.size(); ll up=1,dn=1; vector<int> c(k); for(int j=1;j<=m;j++) { ll x=a[i][j]; for(int u=0;u<k;u++) { while(x%p[u]==0) c[u]++,x/=p[u]; } dn=(lll)dn*x%mod; } for(int j=1;j<=m;j++) { ll x=b[j]; for(int u=0;u<k;u++) { while(c[u]>0&&x%p[u]==0) c[u]--,x/=p[u]; } up=(lll)up*x%mod; } bool fl=1; for(int j=0;j<k;j++) if(c[j]>0) {fl=0; break;} if(!fl) { puts("-1"); continue; } ll res=(lll)up*inv(dn,mod)%mod; printf("%lld\n",res); } return 0; }
- 1
信息
- ID
- 6560
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者