Codeforces 1148G - Gold Experience(分块 bitset 优化 bfs)

首先考虑对一般图 \(G\) 求解。

考虑 \(G\) 的补图,如果补图中连通块个数超过一半,那么从每个连通块中选择一个点出来得到的点集中每个点都是好的。否则在补图的每个连通块中找一棵生成树出来。踢掉所有大小为 \(1\) 的连通块,设剩余连通块还有 \(c\) 个,它们的总和为 \(s\),显然由于孤立点不超过 \(\dfrac{n}{2}\),因此 \(c\le\dfrac{n}{2},s>\dfrac{n}{2}\)。并且剩余连通块中至少剩下一个连通块 \(\ge 3\) 的,如果 \(k\le 2c\),那么分 \(k\) 是偶数和 \(k\) 是奇数处理,如果是偶数选择从 \(\dfrac{k}{2}\) 个连通块中各选 \(2\) 个在补图中相邻的点,如果是奇数就从那个连通块 \(\ge 3\) 的里面选三个点满足它们构成连通块,再从另外 \(\dfrac{k-3}{2}\) 个连通块中各选一对相邻的点。否则就先从每个连通块中选一对相邻的点,再随便另外挑 \(k-2c\) 个点即可。

接下来将这个算法应用于原题上。其他都好办,不难发现最难的地方在于求生成树。到这里为止有很多种做法,大部分做法都是根据本题所求信息的特殊性避开求生成树这一环节而言的,而本人借鉴了 Kubic 神仙的做法。考虑类似于 bitset 优化 BFS 的那种感觉。我们对每个质数开个 bitset 维护有那些元素包含这个质因子,这样我们可以通过 \(\omega(V)\) 次 bitset 操作查找一个点在补图中的邻居。但是这样空间有点寄,类比分块 bitset,将质数分成大质数和小质数处理,碰到小质数暴力求交,碰到大质数再用 bitset 存储,这样就可以过了。

时间复杂度 \(\dfrac{n^2\omega(V)}{64}\)。所以说其实是思想很 simple 的一道题。

const int MAXN=1e5;
const int MAX_PR=6.7e5;
const int MAXV=1e7;
const int LIM=100;
const int MAX_BITSET=MAX_PR/LIM;
int n,k,a[MAXN+5];vector<int>vec[MAX_PR+5],pf[MAXN+5];
int pr[MAX_PR+5],prcnt=0,mnp[MAXV+5],id[MAXV+5],vis[MAXV+5];
void sieve(int n){
	for(int i=2;i<=n;i++){
		if(!vis[i])pr[++prcnt]=i,mnp[i]=i,id[i]=prcnt;
		for(int j=1;j<=prcnt&&pr[j]*i<=n;j++){
			vis[pr[j]*i]=1;mnp[pr[j]*i]=pr[j];
			if(i%pr[j]==0)break;
		}
	}
}
bitset<MAXN+5>used,occ[MAX_BITSET+5],N;
int pos[MAX_PR+5],r_pos[MAX_PR+5],cnt,f[MAXN+5],siz[MAXN+5];
int find(int x){return (!f[x])?x:f[x]=find(f[x]);}
void merge(int x,int y){x=find(x);y=find(y);f[x]=y;siz[y]+=siz[x];}
int hd[MAXN+5],to[MAXN*2+5],nxt[MAXN*2+5],ec=0;
void adde(int u,int v){to[++ec]=v;nxt[ec]=hd[u];hd[u]=ec;}
vector<int>seq;
void dfs(int x,int f){seq.pb(x);for(int e=hd[x];e;e=nxt[e])if(to[e]!=f)dfs(to[e],x);}
int main(){
#ifdef LOCAL
	freopen("in.txt","r",stdin);
	freopen("out.txt","w",stdout);
#endif
	scanf("%d%d",&n,&k);sieve(MAXV);
	for(int i=1;i<=n;i++)N[i]=1;
	for(int i=1;i<=n;i++)scanf("%d",&a[i]);
	for(int i=1;i<=n;i++){
		int tmp=a[i];
		while(tmp^1){
			int p=mnp[tmp];vec[id[p]].pb(i);pf[i].pb(p);
			while(tmp%p==0)tmp/=p;
		}
	}
	// for(int i=1;i<=n;i++){printf("%d:",i);for(int p:pf[i])printf(" %d",p);printf("\n");}
	for(int i=1;i<=prcnt;i++)if(vec[i].size()>LIM){
		pos[++cnt]=i;r_pos[i]=cnt;
		for(int x:vec[i])occ[cnt][x]=1;
		occ[cnt]^=N;
	}
	for(int i=1;i<=n;i++)siz[i]=1;used=N;
	for(int i=1;i<=n;i++)if(used[i]){
		queue<int>q;q.push(i);used[i]=0;
		while(!q.empty()){
			int x=q.front();q.pop();bitset<MAXN+5>can=used;
			for(int p:pf[x]){
				if(vec[id[p]].size()<=LIM){for(int y:vec[id[p]])can[y]=0;}
				else can&=occ[r_pos[id[p]]];
			}
			for(int y=can._Find_first();y!=can.size();y=can._Find_next(y))
				used[y]=0,merge(x,y),adde(x,y),adde(y,x),q.push(y);
		}
	}
	vector<int>vec;
	for(int i=1;i<=n;i++)if(find(i)==i)vec.pb(i);
	if(vec.size()>=k){
		for(int i=0;i<k;i++)printf("%d ",vec[i]);
		printf("\n");
	}else{
		vector<pii>cmps;
		for(int i=1;i<=n;i++)if(find(i)==i&&siz[i]>1)cmps.pb(mp(siz[i],i));
		sort(cmps.begin(),cmps.end(),greater<pii>());
		int sum=0;
		for(int i=0;i<cmps.size();i++){
			sum+=cmps[i].fi;
			if((i+1)*2<=k&&k<=sum){
				vector<int>v,res;int nd=k-(i+1)*2;
				for(int j=0;j<=i;j++)v.pb(2);
				for(int j=0;j<=i;j++)if(nd){
					if(nd>=cmps[j].fi-2)v[j]=cmps[j].fi,nd-=(cmps[j].fi-2);
					else v[j]+=nd,nd=0;
				}
				for(int j=0;j<=i;j++){
					seq.clear();dfs(cmps[j].se,0);
					for(int k=0;k<v[j];k++)res.pb(seq[k]);
				}
				for(int x:res)printf("%d ",x);printf("\n");
				return 0;
			}
		}
	}
	return 0;
}
posted @ 2022-12-23 18:49  tzc_wk  阅读(69)  评论(0)    收藏  举报