给你正整数 n n n,让你求任意一个 X X X,满足从 X X X开始的连续 n n n个整数通过以下条件联通:
对于两个数 u u u和 v v v,假如 u u u和 v v v不互质,那么它们之间就连上一条边。
n ≤ 100000 n\leq 100000 n≤100000
(记得打高精度)
花了半天时间终于把题目换成了一个可以看的模型:
对于所有小于 n n n的质数 p i p_i pi, p i p_i pi的所有倍数(形如 X + i X+i X+i)都连在一起。
题目的条件是 X + i ≡ X + j ≡ 0 ( m o d p i ) X+i \equiv X+j \equiv 0 (\mod p_i) X+i≡X+j≡0(modpi)
简单推一下就是 i ≡ j ≡ − X ( m o d p i ) i\equiv j \equiv -X(\mod p_i) i≡j≡−X(modpi)
(接下来为了方便,我直接将 − X -X −X变成 X X X)
于是就有了更好看的模型:对于数列 0.. n − 1 0..n-1 0..n−1,对于每个质数 p i p_i pi,钦定一个数 a i a_i ai,表示所有满足 x ≡ a i ( m o d p i ) x\equiv a_i (\mod p_i) x≡ai(modpi)的都连在一起。
至于 X X X,做中国剩余定理就可以求出来了。
现在的问题时怎么构造,而我不会……
所以就暴力构造,如果合法就用中国剩余定理求。
得分和纯暴力没有什么两样。
先说一说一个更加优美些的暴力该怎么做。
对于某个大于 n 2 \frac{n}{2} 2n的质数 p i p_i pi,很显然它只能连接两个点。
由于考虑 p 1 p_1 p1(也就是 2 2 2)的时候就已经将距离为偶数的点连起来了,所以对于某个奇素数 p i p_i pi连向了某个数 x x x,如果它没有在 p 1 p_1 p1的时候被连到,那么它连向的 x + p i x+p_i x+pi(或 x − p i x-p_i x−pi)一定在 p 1 p_1 p1的时候被连到了(因为 p i p_i pi为奇数)
所以,在暴力了 n 2 \frac{n}{2} 2n之前的质数之后,剩余的质数贪心地去捡漏就好了。
题解说这样可以过掉 n ≤ 40 n\leq 40 n≤40,但实测出只能过 n ≤ 31 n\leq 31 n≤31。
接下来就是题解的奇妙构造大法。
考虑假如我们把第一个点(也就是 0 0 0)作为核心点(就是 a i ≡ 0 ( m o d p i ) a_i\equiv 0 (\mod p_i) ai≡0(modpi)),就会发现除了第二个点( 1 1 1)之外,其它的点都会被接上。
于是我们考虑怎么让它被连到。
然而在这个情况下,已经没有素数供我们使用了。
考虑将核心点放在中间(记为 m i d mid mid),容易发现只有 m i d − 1 mid-1 mid−1和 m i d + 1 mid+1 mid+1没有被连上。
找到最大的小于 n 2 \frac{n}{2} 2n的两个素数,分别将 m i d − 1 mid-1 mid−1和 m i d + 1 mid+1 mid+1连上。
由于这两个素数被侵占了,所以会多出几个位置没有连上。
n 2 \frac{n}{2} 2n以上的素数我们还没有用过,那就随便连连即可。
接下来有个问题:有没有可能素数不够用??
实践表明,有这种情况……在 n ≤ 31 n\leq 31 n≤31的时候还是自己跑暴力吧。
不过更大的数,似乎都可以(这就要靠实践了……)
我猜想 n > 31 n>31 n>31都可以用这个方式构造出来,但是不会证。
如果能证的话,GMH大爷提供了一条定理,可以参考一下:
伯特兰-切比雪夫定理
SRC Download
using namespace std;
#include
#include
#include
#include
#include
#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;
}
实践出真知。