JZOJ6383. 【NOIP2019模擬2019.10.07】果實摘取

Description

  • 小 D 的家門口有一片果樹林,果樹上果實成熟了,小 D 想要摘下它們。
    爲了便於描述問題,我們假設小 D 的家在二維平面上的 (0, 0) 點,所有座標範圍的絕對值不超過 N 的整點座標上都種着一棵果樹。((0, 0) 這個點沒有果樹)
  • 小 D 先站在 (0, 0) 處,正對着 (1, 0) 的方向。
  • 每次摘果實時,小 D 會逆時針選擇他能看到的第 K 棵還未摘取果實的果樹,然後向着這個方向走去,在行走的過程中摘下沿路的所有的果樹上的果樹果實,直到走到果樹林的邊緣。
  • 接下來,小 D 回到 (0, 0) 處,正對着上一次摘果實的果樹的方向。
  • 小 D 會重複這個過程,直到所有的果實都被摘取,小 D 感興趣的是,最後一棵被摘下果實的果樹是哪一棵?
  • 注意小 D 不能看到被任何其他果樹遮擋着的果樹。
  • n,K<=1e5

Solution

  • 考慮一共有多少條果樹,也就是
    (i=1nϕ(i)+1)8(\sum_{i=1}^{n}\phi (i)+1)*8
  • 接下來每一次跳K就是一個經典的約瑟夫問題了。
  • 有一種O(n)O(n)的遞推做法
  • f[i]=(f[i1]+K)mod  i,f[1]=0f[i]=(f[i-1]+K) \mod i ,f[1]=0
  • 意義就是考慮長度爲i的約瑟夫問題,欽定從0開始,選擇掉了K-1,剩下的是一個i-1的子問題,集合是k,k+1...n1,0,1...,k2{k,k+1...n-1,0,1...,k-2},這個子問題的編號加上k就是當前的編號了。
  • 由於n(此n非讀入的n)非常大,發現如果多次加K都沒有取模得話,我們可以將這些壓在一起做,推一推算一算就變成log的了。
  • 那麼問題轉化成爲求斜率第x個的互質的座標。
  • 先可以給x對於一個象限裏的個數去一個模。
  • 然後二分一個小數表示最大斜率mid,暴力枚舉橫座標,計算縱座標滿足在斜率內的互質點的個數。
  • 即對於每一個i,求出:
    j=1imid[gcd(i,j)=1]\sum_{j=1}^{i*mid}[gcd(i,j)=1]
  • 容(fan)斥(yan)一下就可以得到
    j=1imiddgcd(i,j)μ[d]\sum_{j=1}^{i*mid} \sum_{d|gcd(i,j)} \mu[d]
  • 將d提前
    diμ[d]imid/d\sum_{d|i}\mu[d]*\left \lfloor \left \lfloor i*mid\right \rfloor /d \right \rfloor
  • 對於每一i枚舉它的因子,總複雜度即爲n/1+n/2+n/3+n/4…
  • O(nlogn)O(n logn )
  • 加上二分O(nlog2n)O(n log ^2 n )
  • 很短很好打
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#define maxn 100005
#define db double 
#define E 3e-10
#define min(a,b) ((a<b)?a:b)
#define ll long long 
using namespace std;

int n,m,i,j,tp,xx,yy;
int tot,pri[maxn],bz[maxn],phi[maxn],u[maxn];
ll sum,sum0,num,f,x,d,k;
db l,r,mid;

int gcd(int x,int y){return (x%y==0)?y:gcd(y,x%y);}

void Getphi(){
	u[1]=1;
	for(i=2;i<=n;i++) {
		if (!bz[i]) bz[i]=1,u[i]=-1,phi[i]=i-1,pri[++tot]=i;
		for(j=1;j<=tot&&i*pri[j]<=n;j++){
			bz[i*pri[j]]=1;
			if (i%pri[j]==0){
				phi[i*pri[j]]=phi[i]*pri[j];
				u[i*pri[j]]=0;
				break;
			}  else {
				phi[i*pri[j]]=(pri[j]-1)*phi[i];
				u[i*pri[j]]=-u[i];
			}
		}
	}
}

int main(){
	scanf("%d%d",&n,&m);
	Getphi();
	for(i=1;i<=n;i++) sum+=phi[i];
	sum++,sum0=sum*8;
	f=0,x=1;
	while (x<sum0){
		d=min(sum0-x,(x-f)/(m-1)+((x-f)%(m-1)>0));
		f=(f+d*m)%(x+d),x+=d;
	} 
	tp=f/(sum*2),num=f%(sum*2);
	if (num==0) xx=n,yy=0; else {
		l=0,r=n;
		while (abs(r-l)>E){
			 mid=(l+r)/2;
			 sum=0;
			 for(d=1;d<=n;d++) for(i=d;i<=n;i+=d){
			 	k=i*mid; k=min(k,n);
				sum+=k/d*u[d];
			 }
			 if (sum<num) l=mid; else 
			 if (sum>num) r=mid; else {
			 	xx=n,yy=0;
				for(i=1;i<=n;i++) {
					k=i*mid; k=min(k,n);
					if (1.0*k/i>1.0*yy/xx) 
						xx=i,yy=k;
				}
				break;
			 }
		}
	}
	while (tp--) swap(xx,yy),xx=-xx;
	if (xx&&yy){
		k=min(n/abs(xx),n/abs(yy));
		xx*=k,yy*=k;
	}
	printf("%d %d",xx,yy);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章