6455. 【GDOI2020模擬02.01】小 D 的交通

題目

給你正整數nn,讓你求任意一個XX,滿足從XX開始的連續nn個整數通過以下條件聯通:
對於兩個數uuvv,假如uuvv不互質,那麼它們之間就連上一條邊。
n100000n\leq 100000
(記得打高精度)


思考歷程

花了半天時間終於把題目換成了一個可以看的模型:
對於所有小於nn的質數pip_ipip_i的所有倍數(形如X+iX+i)都連在一起。
題目的條件是X+iX+j0(mod  pi)X+i \equiv X+j \equiv 0 (\mod p_i)
簡單推一下就是ijX(mod  pi)i\equiv j \equiv -X(\mod p_i)
(接下來爲了方便,我直接將X-X變成XX
於是就有了更好看的模型:對於數列0..n10..n-1,對於每個質數pip_i,欽定一個數aia_i,表示所有滿足xai(mod  pi)x\equiv a_i (\mod p_i)的都連在一起。
至於XX,做中國剩餘定理就可以求出來了。
現在的問題時怎麼構造,而我不會……
所以就暴力構造,如果合法就用中國剩餘定理求。
得分和純暴力沒有什麼兩樣。


正解

先說一說一個更加優美些的暴力該怎麼做。
對於某個大於n2\frac{n}{2}的質數pip_i,很顯然它只能連接兩個點。
由於考慮p1p_1(也就是22)的時候就已經將距離爲偶數的點連起來了,所以對於某個奇素數pip_i連向了某個數xx,如果它沒有在p1p_1的時候被連到,那麼它連向的x+pix+p_i(或xpix-p_i)一定在p1p_1的時候被連到了(因爲pip_i爲奇數)
所以,在暴力了n2\frac{n}{2}之前的質數之後,剩餘的質數貪心地去撿漏就好了。
題解說這樣可以過掉n40n\leq 40,但實測出只能過n31n\leq 31

接下來就是題解的奇妙構造大法。
考慮假如我們把第一個點(也就是00)作爲核心點(就是ai0(mod  pi)a_i\equiv 0 (\mod p_i)),就會發現除了第二個點(11)之外,其它的點都會被接上。
於是我們考慮怎麼讓它被連到。
然而在這個情況下,已經沒有素數供我們使用了。
考慮將核心點放在中間(記爲midmid),容易發現只有mid1mid-1mid+1mid+1沒有被連上。
找到最大的小於n2\frac{n}{2}的兩個素數,分別將mid1mid-1mid+1mid+1連上。
由於這兩個素數被侵佔了,所以會多出幾個位置沒有連上。
n2\frac{n}{2}以上的素數我們還沒有用過,那就隨便連連即可。
接下來有個問題:有沒有可能素數不夠用??
實踐表明,有這種情況……在n31n\leq 31的時候還是自己跑暴力吧。
不過更大的數,似乎都可以(這就要靠實踐了……)
我猜想n>31n>31都可以用這個方式構造出來,但是不會證。
如果能證的話,GMH大爺提供了一條定理,可以參考一下:
伯特蘭-切比雪夫定理


代碼


SRC Download 
using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cstdlib>
#include <cmath>
#define N 100010
#define ll long long
ll qpow(ll x,int y,int mo){
	ll res=1;
	for (;y;y>>=1,x=x*x%mo)
		if (y&1)
			res=res*x%mo;
	return res;
}
int n;
int p[N],np;
bool inp[N];
int a[N];
int dsu[N];
int getdsu(int x){return dsu[x]==x?x:dsu[x]=getdsu(dsu[x]);}
#define BIT 1000000
struct Bigint{
	int k;
	ll v[N];
	void print(){
		printf("%lld",v[k]);
		for (int i=k-1;i>=0;--i)
			printf("%06lld",v[i]);
	}
};
inline void operator+=(Bigint &A,Bigint &B){
	for (int i=0;i<=A.k || i<=B.k;++i){
		A.v[i]+=B.v[i];
		A.v[i+1]+=A.v[i]/BIT;
		A.v[i]%=BIT;
	}
	A.k=max(A.k,B.k);
	if (A.v[A.k+1])
		A.k++;
}
inline void operator*=(Bigint &A,int x){
	for (int i=0;i<=A.k;++i)
		A.v[i]*=x;
	for (int i=0;i<=A.k;++i){
		A.v[i+1]+=A.v[i]/BIT;
		A.v[i]%=BIT;
	}
	if (A.v[A.k+1])
		A.k++;
}
inline void operator*=(Bigint &A,Bigint &B){
	static Bigint C;
	memset(C.v,0,sizeof(ll)*(A.k+B.k+1+1));
	for (int i=0;i<=A.k;++i)
		for (int j=0;j<=B.k;++j)
			C.v[i+j]+=A.v[i]*B.v[j];
	for (int i=0;i<=A.k+B.k;++i){
		C.v[i+1]+=C.v[i]/BIT;
		C.v[i]%=BIT;
	}
	C.k=A.k+B.k;
	if (C.v[C.k+1])
		C.k++;
	A.k=C.k;
	memcpy(A.v,C.v,sizeof(ll)*(C.k+1));
}
Bigint pro,sum,Mi;
int cnt,ai[N],mi[N];
void calc(int mid){
	pro.k=0;
	pro.v[0]=1;
	for (int i=1;i<=np;++i)
		if (a[i]==0)
			pro*=p[i];
		else{
			++cnt;
			ai[cnt]=a[i];
			mi[cnt]=p[i];
		}
	for (int i=1;i<=cnt;++i){
		memset(Mi.v,0,sizeof(ll)*(Mi.k+1));
		Mi.k=0,Mi.v[0]=1;
		ll ti=1;
		for (int j=1;j<=cnt;++j)
			if (i!=j){
				Mi*=mi[j];
				ti=ti*mi[j]%mi[i];
			}
		Mi*=pro;
		ll tmp=0;
		for (int j=pro.k;j>=0;--j)
			tmp=(tmp*BIT+pro.v[j])%mi[i];
		ti=ti*tmp%mi[i];
		ti=qpow(ti,mi[i]-2,mi[i]);
		Mi*=ti;
		Mi*=ai[i];
		sum+=Mi;
	}
	sum.v[0]-=mid;
	for (int i=0;sum.v[i]<0;++i)
		sum.v[i]+=BIT,sum.v[i+1]--;
	if (sum.v[sum.k]==0)
		sum.k--;
	sum.print();
}
//////////////////
int top[40][N];
int gettop(int num,int x){return top[num][x]==x?x:top[num][x]=gettop(num,top[num][x]);}
void dfs(int k){
	if (k>np || p[k]>n>>1){
		for (int i=np;i>=k;--i){
			for (int j=0;j<n;++j)
				if ((j-p[i]>=0 || j+p[i]<n) && j%2!=a[1] && gettop(k-1,j)==j){
					if (j-p[i]>=0)
						top[k-1][gettop(k-1,j)]=gettop(k-1,j-p[i]);
					else if (j<gettop(k-1,j+p[i]))
						top[k-1][gettop(k-1,j+p[i])]=gettop(k-1,j);
					else
						top[k-1][gettop(k-1,j)]=gettop(k-1,j+p[i]);
					a[i]=j%p[i];
					a[i]=(a[i]?p[i]-a[i]:0);
					break;
				}
		}
		for (int i=0;i<n;++i)
			if (gettop(k-1,i)!=0)
				return;
		calc(0);
		exit(0);
	}
	for (int i=0;i<p[k];++i){
		memcpy(top[k],top[k-1],sizeof(int)*n);
		a[k]=(i?p[k]-i:0);
		for (int j=i;j+p[k]<n;j+=p[k]){
			int x=gettop(k,j),y=gettop(k,j+p[k]);
			if (x>y)
				swap(x,y);
			top[k][y]=x;
		}
		dfs(k+1);
	}
}
////////////////
int main(){
	freopen("teleports.in","r",stdin);
	freopen("teleports.out","w",stdout);
	scanf("%d",&n);
	if (n==1){
		printf("1\n");
		return 0;
	}
	if (n<17){
		printf("No solution\n");
		return 0;
	}
	for (int i=2;i<n;++i){
		if (!inp[i])
			p[++np]=i;
		for (int j=1;j<=np && i*p[j]<n;++j){
			inp[i*p[j]]=1;
			if (i%p[j]==0)
				break;
		}
	}
	if (n<32){
		for (int i=0;i<n;++i)
			top[0][i]=i;
		dfs(1);
		return 0;
	}
	int mid=n/2,used;
	for (int i=1;i<=np && p[i]<=mid;++i)
		a[i]=0,used=i;
	a[used]=(-1+p[used])%p[used];
	a[used-1]=1;
	for (int i=0;i<n;++i)
		dsu[i]=i;
	for (int i=1;i<=used;++i){
		int tmp=getdsu(mid+a[i]-p[i]);
		for (int j=mid+a[i];j<n;j+=p[i])
			dsu[getdsu(j)]=tmp;
		for (int j=mid+a[i]-p[i]*2;j>=0;j-=p[i])
			dsu[getdsu(j)]=tmp;	
	}
	int mai=getdsu(mid);
	for (int i=0;i<n;++i)
		if (getdsu(i)!=mai){
			used++;
			a[used]=(i-mid)%p[used];
			a[used]=(a[used]<0?a[used]+p[used]:a[used]);
			dsu[i]=mai;
		}
	for (int i=1;i<=np;++i)
		a[i]=(a[i]?p[i]-a[i]:0);
	calc(mid);
	return 0;
}

總結

實踐出真知。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章