本文迁移自洛谷原文 。
自己的题,怎么能不写题解呢?
考察点:构图
我们可以新建$n$个点:$n+1 \dots2n$
第$i$个点表示$i-n$这个数
对于所有的$a_i$,将i与所有是$a_i$的约数的平方数的平方根所对应的节点号连一条边,权值为这个平方根
这样就可以转换成一个图论问题了!
也就是说,对于这张图,选出最大数量的点,使得两两之间不能由权值大于$x$的边相连。
我们很容易就可以发现, 在一个由权值大于$x$所组成的联通快中,最多只能选择一个。
所以跑$n$边dfs就可以了!
注意点1:
由于有$2 \times 10^5$个点和询问,所以裸的$O(nm)$是过不了的
通过打表可以发现,有很大一部分询问的答案是相同的
最多只需要查找$\sqrt{max{a_i}}=200$次即可。
所以只需要找到这个临界值就可以了
2.如何O($b_i^{\frac{1}{3}}$)求出$c_i$?
1 2 3 4 5 6 7 8 9 10 11 res=1 ;i=1 while i*i*i<=b[i]: cnt=0 while b[i]%i==0 : cnt=cnt+1 b[i]=b[i]/i res=max (res,cnt) last_b=b[i] tmp=sqrt (b[i]) if tmp*tmp==b[i]:res=max (res,2 )return res
为什么这段代码是正确的?
假设 $last_b=p*q$
因为所有$\le b_i^{\frac{1}{3}}$的 因数都已经被while除完了
所以$p,q$一定为两个挺大的,互质的数
所以只要特判一下$p,q$是否相等即可
std:
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 #include <bits/stdc++.h> #define mp make_pair #define ll long long using namespace std;const int mxn=1000005 ;int n,m;int a[mxn],b[mxn],c[mxn];vector<int >g[mxn]; int overban,t_o,mx;bool use[mxn];inline void dfs (int x) { if (use[x])return ; use[x]=1 ; if (x<=n)mx=max (mx,c[x]); for (int it=0 ;it<g[x].size ();++it){ int y=g[x][it]; int ma=max (x,y); if (ma<=overban)continue ; dfs (y); } } inline pair<int ,int > solve (int xx) { overban=xx; t_o=xx-n; int res=0 ,cnt=0 ; memset (use,0 ,sizeof (use)); for (int i=1 ;i<=n;++i){ if (!use[i]){ mx=0 ; dfs (i); if (mx)++cnt; res+=mx; } } return mp (cnt,res); } pair<int ,int >ans[mxn]; int main () { scanf ("%d%d" ,&n,&m); for (int i=1 ;i<=n;++i){ scanf ("%d" ,a+i); for (int x=2 ;x*x<=a[i];++x){ if (a[i]%(x*x)==0 ){ g[i].push_back (n+x); g[n+x].push_back (i); } } } for (int i=1 ;i<=n;++i){ scanf ("%d" ,b+i); int t=b[i],res=1 ; for (int x=2 ;x*x*x<=t;++x){ int c=0 ; for (;t%x==0 ;t/=x)++c; res=max (res,c); } int t2=sqrt (t); if (t2*t2==t)res=max (res,2 ); else res=max (res,1 ); c[i]=res; } for (int i=1 ;i<=200000 ;++i){ if (ans[i-1 ].first==n){ ans[i]=ans[i-1 ]; continue ; } ans[i]=solve (i+n); } for (int i=1 ;i<=m;++i){ int x;scanf ("%d" ,&x); printf ("%d %d\n" ,ans[x].first,ans[x].second); } return 0 ; }