P6583 回首過去--整數分塊

題目來源:P6583 回首過去.
根據樣例分析,可以xy\frac xy是有限小數的條件是分母只能包含因子2和5,直覺證明,整數在進行除法的過程中,如果需要去補0,則相當於*10,而10只包含因子2和5,所以出現其他因子就不是有限循環小數。

40分 n*n暴力

不解釋

80分

符合條件的xy\frac xy可以分解爲XY2p5qX\frac{XY}{2^p5^qX}其中KaTeX parse error: Expected '}', got 'EOF' at end of input: {X%2||X%5!=0},
A=2p5qA=2^p5^q,,這樣我們有三個變量:在A、X、Y的個數。
且有隱含條件:AX<=n,XY<=n.
我們在A,Y確定的情況統計X的個數,A的取值範圍大約爲log2log5log2*log5
例如
n=20,A=1,2,4,5,8,10,16,20,
根據X的取值特點,X=1,3,7,9,11,13,17,19。
可以用容斥函數計算X允許的取值情況fc(X)=XX2X5+X10fc(X)=X-\frac X2-\frac X5+\frac X{10}
舉個實例來感性理解下。

A=1 時候

Y=1——》X=1…n/A;1,3,7,9,11,13,17,19
Y=2——》X=1…n/2;1,3,7,9,
Y=3——》X=1…n/3;1,3,
Y=4——》X=1…n/4;1,3,
Y=5——》X=1…n/5;1,3,
Y=6——》X=1…n/6;1,3,
Y=7——》X=1…n/7;1

A=2 時候

Y=1——》X=1…n/2;1,3,7,9
Y=2——》X=1…n/2;1,3,7,9,
Y=3——》X=1…n/3;1,3,
Y=4——》X=1…n/4;1,3,
Y=5——》X=1…n/5;1,3,
Y=6——》X=1…n/6;1,3,
Y=7——》X=1…n/7;1

設f(Y)爲A,Y確定時候符合要求想X數的個數。

f(y)={fc(nA)Y<=Afc(nY)Y>Af(y)= \begin{cases} fc( \frac{n}A) & \text{Y<=A}\\ fc(\frac nY)& \text{Y>A} \end{cases}
對於每個確定的A ,Y=1nfc(Y)=Afc(nA)+Y=A+1nfc(nY)\sum_{Y=1}^nfc(Y)=A*fc(\frac nA)+\sum_{Y=A+1}^nfc(\frac nY),,Y=A+1nfc(nY\sum_{Y=A+1}^nfc(\frac nY可以用一個後綴和維護。
然後枚舉每個A,計算,總時間複雜度O(nlog2log5)O(n*log2*log5)

//tjn,https://www.cnblogs.com/chdy/p/13009533.html
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll MAXN=10010;
ll n,ans,cnt;
ll a[MAXN],s[10000009];
inline ll calc(ll x) {
	return x-x/2-x/5+x/10;
}

ll work(ll x){//fy=  n/t;n/y  
	ll w1=n/x;     
	ll t=n/w1,ans=0;
	ans=t*calc(n/x);
	ans+=s[t+1];
/*	for(int i=t;i<=n;i++){
		ans+=calc(n/i);
		cout<<"------------"<<i<<" "<<n/i<<" calc="<<calc(n/i)<<endl;
	}
	cout<<x<<":"<<ans<<endl; 
	*/
	return ans;
}
signed main() {

	cin>>n;
	for(ll i=1; i<=n; i=i*2)
		for(ll j=1; i*j<=n; j=j*5)a[++cnt]=i*j;
	sort(a+1,a+1+cnt);
	for(int i=n;i>0;i--)
		s[i]=s[i+1]+calc(n/i);
	for(int i=1;i<=cnt;i++){
		ans+=work(a[i]);
	}
	cout<<ans<<endl;
	return 0;
}

100分做法

針對80分的做法。
面對式子XYAX\frac{XY}{AX},前面枚舉了A,Y求X的個數,大量的計算用到了nY\frac nY,數據範圍擴大到101210^{12}後,已經沒法存儲記錄了,每次枚舉A的時候都要分塊計算他,時間複雜21062*10^6,統計了下A的個數是137,那麼總時間複雜大約度31083*10^8,沒有寫,運氣好的話也許能拿這20分。
我們發現用這種方法進行了大量重複的計算nY\frac nY。前面分段函數Y<=A的情況也很有規律。再回到三個變量的式子A,X,Y去分析統計,可以枚舉別的量嗎?

如果我們枚舉X,

符合要求的A的個數就是所有A<=nX{A<=\frac nX},隨着X越來越大,數據A可以排序後統計符合要求的個數。符合要求的A會越來越少,可以單調統計。
符合要求的Y的個數就是nX{\frac nX},因此在枚舉X的情況下還是nX{\frac nX}的計數形式,還是可以分塊。
x=1n(A=1nXnX)\sum_{x=1}^n(\sum _{A=1}^{\frac nX }*\frac nX)
然後分塊計算。
參考代碼:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll MAXN=10010;
ll n,ans,cnt;
ll a[MAXN],s[10000009];
inline ll calc(ll x) {
	return x-x/2-x/5+x/10;
}

ll work(ll x){//fy=  n/t;n/y  
	ll w1=n/x;     
	ll t=n/w1,ans=0;
	ans=(t)*calc(n/x);
	ans+=s[t+1];
	return ans;
}
signed main() {

	cin>>n;
	for(ll i=1; i<=n; i=i*2)
		for(ll j=1; i*j<=n; j=j*5)a[++cnt]=i*j;
	sort(a+1,a+1+cnt);
	ll num=cnt;
	for(ll l=1,r;l<=n;l=r+1){
		r=n/(n/l);
		while(a[num]>n/l&&num)num--;
		//(r-l-1)*(n/l) 
	//	cout<<"num="<<num<<"*("<<calc(r)<<"-"<<calc(l-1)<<")*"<<(n/l)<<"="<<num*(calc(r)-calc(l-1))*(n/l)<<endl; 
		ans+=num*(calc(r)-calc(l-1))*(n/l) ;
			
	
	}
	cout<<ans<<endl;
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章