【Atcoder】 [ARC141D] Non-divisible Set

文章讲述了在Atcoder和Luogu竞赛中的一个问题,涉及如何在特定限制下,选择一个二进制表示的奇数,只允许选择一个。通过计算符合条件的奇数k的区间范围,作者提供了一种高效的时间复杂度为O(nlnn)的解法。
摘要由CSDN通过智能技术生成

题目链接

Atcoder方向
Luogu方向

题目解法

神仙题!
考虑到 a i < 2 m a_i<2m ai<2m 的奇怪限制,可以得到对于 k ∗ 2 i ( k k*2^i(k k2i(k 为奇数 ) ) ) 的形式的所有数中,只能选出 1 1 1 个,且必须选出一个,因为 1 − 2 m 1-2m 12m 的奇数只有 m m m
考虑满足条件的形式是什么,即对于 k 1 ∗ 2 i , k 2 ∗ 2 j k_1*2^i,k_2*2^j k12i,k22j,不存在 i < j i<j i<j k 1 < k 2 k1<k2 k1<k2
这启发我们对每个 i i i 算出符合条件 k k k 的区间范围 L k , R k L_k,R_k Lk,Rk(确实有数的出现的范围)
具体如何求解 L k , R k L_k,R_k Lk,Rk?这里以 L k L_k Lk 举例,只要从大到小枚举 k k k,然后 L k = max ⁡ { L d + 1 } ( k ∣ d ) L_k=\max\{L_d+1\}(k|d) Lk=max{Ld+1}(kd)
然后把 a i a_i ai 查封成 k ∗ 2 i k*2^i k2i 的形式,易知当 L [ i ] ≤ k ≤ R [ i ] L[i]\le k\le R[i] L[i]kR[i] 的时候是有解的,否则无解(还有一些无解条件是不能得到所有的奇数 k k k 或者 L k > R k L_k>R_k Lk>Rk
时间复杂度 O ( n ln ⁡ n ) O(n\ln n) O(nlnn)

#include <bits/stdc++.h>
using namespace std;
const int N=1000100;
int n,m,a[N],L[N],R[N];
bool vis[N];
inline int read(){
	int FF=0,RR=1;
	char ch=getchar();
	for(;!isdigit(ch);ch=getchar()) if(ch=='-') RR=-1;
	for(;isdigit(ch);ch=getchar()) FF=(FF<<1)+(FF<<3)+ch-48;
	return FF*RR;
}
int find(int x){
    for(int i=0,bs=1;;i++,bs<<=1) if(bs>x) return i-1;
}
int ed(){
    for(int i=1;i<=n;i++) puts("No");
    exit(0);
}
int main(){
	n=read(),m=read();
	for(int i=1;i<=n;i++) a[i]=read(),vis[a[i]]=1;
	for(int i=1;i<=m<<1;i+=2){
		bool flg=0;
		for(int j=i;j<=m<<1;j<<=1) if(vis[j]){ flg=1;break;}
		if(!flg) ed();
	}
	for(int i=1;i<=m<<1;i+=2) R[i]=find((m<<1)/i);
	for(int i=1;i<=m<<1;i+=2){
        while(R[i]>=0&&!vis[i<<R[i]]) R[i]--;
		for(int j=3*i;j<=m<<1;j+=i<<1) R[j]=min(R[j],R[i]-1);
    }
	for(int i=(m<<1)-1;i>=1;i-=2){
		for(int j=3*i;j<=m<<1;j+=i<<1) L[i]=max(L[i],L[j]+1);
        while((i<<L[i])<=(m<<1)&&!vis[i<<L[i]]) L[i]++;
    }
    for(int i=1;i<=m<<1;i+=2) if(L[i]>R[i]) ed();
	for(int i=1;i<=n;i++){
		int k=0;
		while(~a[i]&1) a[i]>>=1,k++;
		if(L[a[i]]<=k&&k<=R[a[i]]) puts("Yes");
		else puts("No");
	}
	fprintf(stderr,"%d ms\n",int(1e3*clock()/CLOCKS_PER_SEC));
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值