Pinely Round 3 (Div. 1 + Div. 2) E. Multiple Lamps(思维题 暴力 补写法)

文章讨论了一个编程问题,涉及n盏灯和n个开关,需要在满足特定限制下按开关使最多n/5的灯亮起。当n小于20时,通过预处理和位运算找出所有可能的合法状态并检查约束。对于n大于等于20的情况,直接给出全部灯亮。代码展示了如何解决此问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目

n(n<=2e5)盏灯,编号1到n,一开始全是熄灭的

n个开关,第i个控制着所有i的倍数,按下i的时候,i、2i、...这些灯的状态会被翻转,

m(m<=2e5)个限制,第j条限制形如uj,vj,表示uj这个开关按下之后,vj必须也按下,限制是单向的

找到一种按开关的方案,使得:

1. 至少按了一盏灯的开关

2. 总的亮灯盏数不超过n/5(下取整)个

输出按下的开关的总个数,以及具体按下的每个开关

实际t(t<=1e4)组样例,但保证sumn不超过2e5

思路来源

tourist代码

题解

观察性质可知,如果[1,n]的所有开关都按下,则最终亮灯的,

是因数个数为奇数个的数,即完全平方数,完全平方数个数为sqrt(n)个

而当n>=20时,sqrt(n)<=n/5成立,所以只需要关注n<20的情况

先打表预处理出来可能的集合,

leg[n](n<=20)表示最终亮灯盏数不超过n/5时所有可能的按开关的方式

由于亮灯盏数和按开关的方式是一一对应的(依次考虑亮灭及是否按),

所以,实际合法的状态不会超过\sum C_{n}^{i}(i<=n/5),极限是\sum_{i=1}^{4}C_{20}^{i}

对于n<=20的询问,暴力遍历所有合法状态,依次判断每个约束是否成立即可

由于n<=20,可以用位运算记录一下每盏灯的亮灭及各个开关的选取情况

代码
// Problem: E. Multiple Lamps
// Contest: Codeforces - Pinely Round 3 (Div. 1 + Div. 2)
// URL: https://codeforces.com/contest/1909/problem/E
// Memory Limit: 256 MB
// Time Limit: 3000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define per(i,a,b) for(int i=(a);i>=(b);--i)
typedef long long ll;
typedef double db;
typedef pair<ll,int> P;
#define fi first
#define se second
#define pb push_back
#define dbg(x) cerr<<(#x)<<":"<<x<<" ";
#define dbg2(x) cerr<<(#x)<<":"<<x<<endl;
#define SZ(a) (int)(a.size())
#define sci(a) scanf("%d",&(a))
#define scll(a) scanf("%lld",&(a))
#define pt(a) printf("%d",a);
#define pte(a) printf("%d\n",a)
#define ptlle(a) printf("%lld\n",a)
#define debug(...) fprintf(stderr, __VA_ARGS__)
const int N=2e5+10,M=21;
int t,n,m,u[N],v[N],lg[1<<M],st,st2;
vector<int>leg[M];
void init(){
	rep(i,1,(1<<M)-1){
		lg[i]=lg[i>>1]+1;
	}
	rep(i,1,19){
		int up=1<<i;
		rep(j,1,up-1){
			int vis=0;
			rep(k,1,i){
				if(j>>(k-1)&1){
					for(int l=k;l<=i;l+=k){
						vis^=(1<<l);
					}
				}
			}
			if(__builtin_popcount(vis)<=i/5){
				leg[i].pb(j);
			}
		}
	}
}
void sol(){
	sci(n),sci(m);
	rep(i,1,m){
		sci(u[i]),sci(v[i]);
		u[i]--,v[i]--;
	}
	if(n>=20){
		printf("%d\n",n);
		rep(i,1,n){
			printf("%d%c",i," \n"[i==n]);
		}
		return;
	}
	for(auto w:leg[n]){
		bool ok=1;
		rep(i,1,m){
			if(w>>u[i]&1 && !(w>>v[i]&1)){
				ok=0;
				break;
			}
		}
		if(ok){
			int sz=__builtin_popcount(w);
			printf("%d\n",sz);
			rep(j,0,sz-1){
				printf("%d%c",lg[w&(-w)]," \n"[j==sz-1]);
				w-=w&(-w);
			}
			return;
		}
	}
	puts("-1");
}
int main(){
	init();
	//sieve();
	sci(t); // t=1
	while(t--){
		sol();
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小衣同学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值