AtCoder Regular Contest 141 D,E

D
很有趣的一题。

首先注意到一个不同寻常的条件MN2M,这启发我们去想有关奇偶的结论。

我们将每个数剖开所有2的因子,即ai=bi2ci,其中bi是奇数。比如ai=12=3×22,此时bi=3,ci=2

那么若对于i,j,且ijbi=bj,那么我们只能选择其中的一个。(因为它们互为倍数)

也就是,对于每个b,我们只能选择一个,而我们现在最少要选择n2个(N2M),且b最大也就2M1ai2M)。所以对于每个不同的b,我们必须从中选择一个

如果对于奇数i满足1i2M,且没有任何一个j,满足bj=i,那无论如何我们都选择不了M个,所以全部输出No

但注意,比如像bi=1bj=3之间也可能出现倍数关系(比如26),这里,若bi<bj,且bibj的因子,那么cj必须小于ci,。(比如我们不能选择26,因为ci=1,cj=1;而我们可以选择46,因为ci=2,cj=1

那么我们就可以根据倍数关系建图,进行拓扑排序,得到当b=ici的取值范围(最大值和最小值)。如果有任何一个b=ici的取值范围是空的话(即最小取值大于最大取值),意味着b=i时我们没法选择,此时也全部输出No

否则我们就判断ci是否在bi时的取值范围内部,输出YesNo

时间复杂度为O(nlogn)

#include<bits/stdc++.h>
#define debug(...) std::cerr<<#__VA_ARGS__<<" : "<<__VA_ARGS__<<std::endl

const int maxn=600005;
int n,m;
int a[maxn],pw[maxn];
int mx[maxn],in[maxn];
int mn[maxn],rin[maxn];
std::vector<int> vec[maxn],g[maxn],rg[maxn];

int main() {
	scanf("%d%d",&n,&m);
	for(int i=1,num;i<=n;i++) {
		scanf("%d",&a[i]);
		pw[i]=1,num=a[i];
		while(num%2==0) num/=2,pw[i]+=pw[i];
		vec[a[i]/pw[i]].push_back(pw[i]);
	}
	for(int i=1;i<=m+m;i+=2) {
		if(vec[i].empty()) {
			for(int i=1;i<=n;i++) printf("No\n");
			exit(0);
		}
		std::sort(vec[i].begin(),vec[i].end());
	}
	for(int i=1;i<=m+m;i++) {
		for(int j=i+i;j<=m+m;j+=i) {
			//从i连向一个通往j的边
			if(i%2==0||j%2==0) continue;//下标只能是奇数 
			g[i].push_back(j); in[j]++;
			rg[j].push_back(i); rin[i]++;
		}
	}
	std::queue<int> q;
	for(int i=1;i<=m+m;i+=2) {
		mx[i]=vec[i].back();
		if(!in[i]) q.push(i);
	}
	while(!q.empty()) {
		int pos=q.front(); q.pop();
		for(auto to : g[pos]) {
			int p=std::lower_bound(vec[to].begin(),vec[to].end(),mx[pos])-vec[to].begin()-1;
			if(p>=0) {
				mx[to]=std::min(mx[to],vec[to][p]);
			} else {
				mx[to]=-1;//impossible
			}
			in[to]--;
			if(!in[to]) q.push(to);
		}
	}
	for(int i=1;i<=m+m;i+=2) {
		mn[i]=vec[i][0];
		if(!rin[i]) q.push(i);
	}
	while(!q.empty()) {
		int pos=q.front(); q.pop();
		for(auto to : rg[pos]) {
			int p=std::upper_bound(vec[to].begin(),vec[to].end(),mn[pos])-vec[to].begin();
			if(p>=vec[to].size()) {
				mn[to]=1e9;
			} else {
				mn[to]=std::max(mn[to],vec[to][p]);
			}
			rin[to]--;
			if(!rin[to]) q.push(to);
		}
	}
	for(int i=1;i<=m+m;i+=2) {
		if(mn[i]==1e9||mx[i]==-1) {
			for(int j=1;j<=n;j++) printf("No\n");
			exit(0);
		}
	} 
	for(int i=1;i<=n;i++) {
		int ind=a[i]/pw[i];
		if(mn[ind]<=pw[i]&&pw[i]<=mx[ind]) {
			printf("Yes\n");
		} else {
			printf("No\n");
		}
	}
	return 0;
}
/*
6 6 
4 5 12 7 9 11
*/
posted @   Nastia  阅读(29)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话
点击右上角即可分享
微信分享提示