【Codeforces 653G】Move by Prime
由于每个质数之间的操作是相互独立的,所以把每个质数分开考虑。把每个数都质因子分解一遍,时间复杂度是O(n√n)。
官方的题解没有讲完,至今不知道它的从k个数到k+1个数是怎么转移的...
对于质数p,第i个数可表示成ti=pai⋅k的形式,其中k与p互质,然后将数组a排序。
接下来要做的就是从数组a中选出一些数,进行最少的+1,-1的操作使得选出的这些数最终相等。
显然,我们要让选出的数最终都等于它们的中位数的时候操作次数最少。
例如,当我们选出的数为b1,b2,b3,⋯,bm时,最少操作次数等于mid−1∑i=1bmid−bi+m∑i=mid+1bi−bmid
容易发现,一个数的贡献等于它被加上的次数减去它被减去的次数,乘以这个数本身的值。
接下来要算的就是一个数最终被加上和减去了多少次。
考虑ak,如果在ak左边有l个数,在ak右边有r个数,那么
1.当l<r时,ak对答案贡献次数+1
2.当l>r时,ak对答案贡献次数-1
3.当l=r时,ak就是中位数,而且加减次数正好抵消,所以对答案没有贡献。
思路比较清晰了,计算ak的贡献,就是计算选k左边的数大于和小于k右边的数的方案个数,然后再乘上ak。
考虑这样一个多项式:(1+x)k−1⋅(1+x−1)n−k=(1+x)n−1xn−k
利用组合的想法,结果的多项式中x的指数即l-r的值,x指数为正即l>r,系数则表示方案数。
然后可以得到第k个数对答案的贡献为ak⋅(Cn−1n−1+Cn−2n−1+⋯+Cn−k+1n−1−Cn−k−1n−1−Cn−k−2n−1−⋯−C0n−1)
最后把一些质数的质数相同的项合并起来瞎JB优化一下就好了。
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 96 | #include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int maxn=300010,p=1e9+7; int Pow( long long x, int pow ) { int res=1; while ( pow ) { if ( pow &1) res=(res*x)%p; x=(x*x)%p; pow >>=1; } return res; } int inv( int x) { return Pow(x,p-2);} int Prime[maxn],Not_Prime[maxn],PToNum[maxn],Primes; int C[maxn],CSum[maxn],CSSum[maxn]; int t[maxn]; int a[maxn][20]; int ans; int n; void Get_Prime() { for ( int i=2;i<maxn;i++) { if (!Not_Prime[i]) Prime[++Primes]=i,PToNum[i]=Primes; for ( int j=1,temp;j<=Primes && (temp=Prime[j]*i)<maxn;j++) { Not_Prime[temp]=1; if (i%Prime[j]==0) break ; } } } void Init_Comb() { C[0]=CSum[0]=CSSum[0]=1; for ( int i=1;i<n;i++) C[i]=(( long long )C[i-1]*(n-i)%p)*inv(i)%p; for ( int i=1;i<n;i++) CSum[i]=(CSum[i-1]+C[i])%p; for ( int i=1;i<n;i++) CSSum[i]=(CSSum[i-1]+CSum[i])%p; } void Init() { scanf ( "%d" ,&n); for ( int i=1;i<=n;i++) scanf ( "%d" ,&t[i]); Get_Prime(); Init_Comb(); for ( int i=1;i<=Primes;i++) a[i][0]=n; for ( int i=1;i<=n;i++) { for ( int j=1;j<=Primes && Prime[j]*Prime[j]<=t[i];j++) if (t[i]%Prime[j]==0) { int cnt=0; while (t[i]%Prime[j]==0) cnt++,t[i]/=Prime[j]; a[j][cnt]++; a[j][0]--; } if (t[i]>1) a[PToNum[t[i]]][1]++,a[PToNum[t[i]]][0]--; } } void Solve() { for ( int i=1;i<=Primes;i++) if (a[i][0]<n) { int cnt=a[i][0]; for ( int j=1;cnt<n;cnt+=a[i][j++]) { int res=0; res=( long long )a[i][j]*CSum[n-1]%p; res=((res+CSum[n-cnt-1]-2*CSSum[n-cnt-1])%p+p)%p; if (cnt+a[i][j]<n) res=((res-CSum[n-cnt-a[i][j]-1]+2*CSSum[n-cnt-a[i][j]-1]%p)%p+p)%p; res=( long long )res*j%p; ans=(ans+res)%p; } } } int main() { Init(); Solve(); printf ( "%d\n" ,ans); } |