zoukankan      html  css  js  c++  java
  • 6455. 【GDOI2020模拟02.01】小 D 的交通

    题目

    给你正整数(n),让你求任意一个(X),满足从(X)开始的连续(n)个整数通过以下条件联通:
    对于两个数(u)(v),假如(u)(v)不互质,那么它们之间就连上一条边。
    (nleq 100000)
    (记得打高精度)


    思考历程

    花了半天时间终于把题目换成了一个可以看的模型:
    对于所有小于(n)的质数(p_i)(p_i)的所有倍数(形如(X+i))都连在一起。
    题目的条件是(X+i equiv X+j equiv 0 (mod p_i))
    简单推一下就是(iequiv j equiv -X(mod p_i))
    (接下来为了方便,我直接将(-X)变成(X)
    于是就有了更好看的模型:对于数列(0..n-1),对于每个质数(p_i),钦定一个数(a_i),表示所有满足(xequiv a_i (mod p_i))的都连在一起。
    至于(X),做中国剩余定理就可以求出来了。
    现在的问题时怎么构造,而我不会……
    所以就暴力构造,如果合法就用中国剩余定理求。
    得分和纯暴力没有什么两样。


    正解

    先说一说一个更加优美些的暴力该怎么做。
    对于某个大于(frac{n}{2})的质数(p_i),很显然它只能连接两个点。
    由于考虑(p_1)(也就是(2))的时候就已经将距离为偶数的点连起来了,所以对于某个奇素数(p_i)连向了某个数(x),如果它没有在(p_1)的时候被连到,那么它连向的(x+p_i)(或(x-p_i))一定在(p_1)的时候被连到了(因为(p_i)为奇数)
    所以,在暴力了(frac{n}{2})之前的质数之后,剩余的质数贪心地去捡漏就好了。
    题解说这样可以过掉(nleq 40),但实测出只能过(nleq 31)

    接下来就是题解的奇妙构造大法。
    考虑假如我们把第一个点(也就是(0))作为核心点(就是(a_iequiv 0 (mod p_i))),就会发现除了第二个点((1))之外,其它的点都会被接上。
    于是我们考虑怎么让它被连到。
    然而在这个情况下,已经没有素数供我们使用了。
    考虑将核心点放在中间(记为(mid)),容易发现只有(mid-1)(mid+1)没有被连上。
    找到最大的小于(frac{n}{2})的两个素数,分别将(mid-1)(mid+1)连上。
    由于这两个素数被侵占了,所以会多出几个位置没有连上。
    (frac{n}{2})以上的素数我们还没有用过,那就随便连连即可。
    接下来有个问题:有没有可能素数不够用??
    实践表明,有这种情况……在(nleq 31)的时候还是自己跑暴力吧。
    不过更大的数,似乎都可以(这就要靠实践了……)
    我猜想(n>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
    ");
    		return 0;
    	}
    	if (n<17){
    		printf("No solution
    ");
    		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;
    }
    

    总结

    实践出真知。

  • 相关阅读:
    关于苹果IPhone/Ipad(IOS)开发者证书申请及安装、真机调试、发布的参考文章
    vs 关闭警告
    真机测试及布署Code Sign error问题总结
    在 Win32 Application 和 Win32 Console Application 中使用 MFC
    获取应用程序路径的区别
    js日期控件
    SQL SERVER 企业管理器 MMC 无法创建管理单元
    进程查看两利器
    用PowerDesigner逆向数据库工程时”Unable to list the table"错误的解决方法
    SQL 附加无日志数据库
  • 原文地址:https://www.cnblogs.com/jz-597/p/12269091.html
Copyright © 2011-2022 走看看