题目描述:
题解:
STO ljx OTZ
下面这个算法是这位贡献的。
不妨将删去改为加入。
那么对于$n=p^k$,即只有一个质因子的$n$来说,若$i$已选,那么$i+n/p$、$i+2*n/p$……都必选。
这个先感性理解一下。
根据这个可以$O(n)$出解。
再看$n=p1^{k1}*p2^{k2}$,即有两个质因子。
对于已选点$i$有两种选择,一种是$i+n/p1$、$i+2*n/p1$……都选,另一种是$i+n/p2$、$i+2*n/p2$……都选。
这个可以将原来的$n$个点分成$n/p1/p2$组,每组都两种选择,即要么分$p1$小组,要么分$p2$小组。
时间复杂度貌似也是$O(n)$??!
代码:
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int N = 20050; template<typename T> inline void read(T&x) { T f = 1,c = 0;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){c=c*10+ch-'0';ch=getchar();} x = f*c; } int n,k,m[10],c; bool vis[N],vis1[N],vis2[N]; void init() { int n0 = n; for(int i=2;i*i<=n0;i++)if(n0%i==0) { m[++c] = i; while(n0%i==0)n0/=i; } if(n0!=1)m[++c]=n0; } int cxk(int x){return x<n?x:x%n;} int sta[N],tl; int main() { read(n),read(k);init(); if(c==1) { int ans = 0; for(int x,i=1;i<=k;i++) { read(x);x--;vis[x]=1; if(!vis1[x]) { vis1[x] = 1;ans += m[1]; int j = cxk(x+n/m[1]); while(j!=x)vis1[j]=1,j=cxk(j+n/m[1]); } } if(ans==n)puts("-1"); else { printf("%d ",ans-k); for(int i=0;i<n;i++)if(!vis[i]&&vis1[i]) printf("%d ",i+1); puts(""); } return 0; }else { for(int x,i=1;i<=k;i++) { read(x),x--,vis[x]=1; if(!vis1[x]) { for(int j=x,k=1;k<=m[1];j=cxk(j+n/m[1]),k++)vis1[j]=1; } if(!vis2[x]) { for(int j=x,k=1;k<=m[2];j=cxk(j+n/m[2]),k++)vis2[j]=1; } } for(int i=0;i<n/m[1]/m[2];i++) { int c1 = 0,c2 = 0; for(int j=i,k=1;k<=m[1]*m[2];j=cxk(j+n/m[1]/m[2]),k++) { if(!vis[j]&&vis1[j])c1++; if(!vis[j]&&vis2[j])c2++; } if(c1<c2) { for(int j=i,k=1;k<=m[1]*m[2];j=cxk(j+n/m[1]/m[2]),k++) if(!vis[j]&&vis1[j])sta[++tl]=j+1; }else { for(int j=i,k=1;k<=m[1]*m[2];j=cxk(j+n/m[1]/m[2]),k++) if(!vis[j]&&vis2[j])sta[++tl]=j+1; } } if(tl+k==n)puts("-1"); else { sort(sta+1,sta+1+tl); printf("%d ",tl); for(int i=1;i<=tl;i++) printf("%d ",sta[i]); puts(""); } } return 0; }