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=1∑nϕ(i)+1)∗8
- 接下來每一次跳K就是一個經典的約瑟夫問題了。
- 有一種O(n)的遞推做法
- f[i]=(f[i−1]+K)modi,f[1]=0
- 意義就是考慮長度爲i的約瑟夫問題,欽定從0開始,選擇掉了K-1,剩下的是一個i-1的子問題,集合是k,k+1...n−1,0,1...,k−2,這個子問題的編號加上k就是當前的編號了。
- 由於n(此n非讀入的n)非常大,發現如果多次加K都沒有取模得話,我們可以將這些壓在一起做,推一推算一算就變成log的了。
- 那麼問題轉化成爲求斜率第x個的互質的座標。
- 先可以給x對於一個象限裏的個數去一個模。
- 然後二分一個小數表示最大斜率mid,暴力枚舉橫座標,計算縱座標滿足在斜率內的互質點的個數。
- 即對於每一個i,求出:
j=1∑i∗mid[gcd(i,j)=1]
- 容(fan)斥(yan)一下就可以得到
j=1∑i∗midd∣gcd(i,j)∑μ[d]
- 將d提前
d∣i∑μ[d]∗⌊⌊i∗mid⌋/d⌋
- 對於每一i枚舉它的因子,總複雜度即爲n/1+n/2+n/3+n/4…
- 即O(nlogn)
- 加上二分O(nlog2n)
- 很短很好打
#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);
}