首页 > 其他分享 >Luogu7740 [NOI2021]机器人游戏 做题记录

Luogu7740 [NOI2021]机器人游戏 做题记录

时间:2024-08-01 21:51:11浏览次数:10  
标签:状态 Luogu7740 15 格子 ll 机器人 NOI2021 define

link 一道炸裂的题目。

首先样例解释已经告诉我们可以容斥。考虑枚举可行的位置集合 \(S\),我们需要统计 \(\forall p\in S\),纸条初始状态和目标状态都相同的方案数。

显然每个机器人独立,可以分开考虑。对于一个机器人,他的行动对纸条的每个格子要么赋值为 \(0/1\),要么不变,要么取反,我们可以用 \(0/1/2/3\) 表示四种状态。

对于一个机器人和一个格子 \(i\),我们统计出格子 \(i\) 能被修改成哪几种状态,即统计对于 \(0/1/2/3\) 四种状态,是否存在 \(p\in S\),满足机器人从格子 \(p\) 出发能令格子 \(i\) 变为该状态。

若一个格子同时存在状态 \(0,1\) 或者同时存在状态 \(2,3\),那么这只能是空格子,有 \(1\) 种方案;若一个格子同时存在 \(0/1\) 和 \(2/3\),那么可以是 \(0/1\) 其中一个数字,或者空格子,有 \(2\) 中方案;若一个格子只存在一种状态,那么 \(3\) 种方案都可以。

当 \(n > 16\) 时会超时。发现 \(n_{max}\le 32\),可以考虑折半算法。又发现机器人走出纸条时会爆炸,容易想到对于步数 \(\ge 16\) 的机器人,如果其纸条非空,那么 \(S\) 只能枚举前 \(16\) 个格子,这就可以直接暴力做了。当然,需要减掉所有步数 \(\ge 16\) 的机器人的纸条全是空的的方案数。

舍弃掉步数 \(\ge 16\) 的机器人,剩下的机器人最多走 \(15\) 步。从前往后考虑每个格子,其可以加入 \(S\) 或否,而该格子贡献的方案数只和前 \(15\) 个格子的状态、这 \(15\) 个格子之前是否有格子加入了 \(S\)、之后是否会有格子加入 \(S\) 三个信息有关。其中后两个信息决定了是否有除了这 \(15\) 个格子的其他格子对其的 \(2\) 状态的影响。

考虑枚举最后一个处于 \(S\) 中的格子,设 \(f[i,S,0/1]\) 表示考虑了前 \(i\) 个格子,其中前 \(15\) 个格子的状态为 \(S\),在这 \(15\) 个格子之前是否有格子加入了集合,此时前 \(i\) 个格子的方案数。

综上时间复杂度为 \(O(2^{\frac n2}n(n+m))\),难以通过。

唯一能优化的地方在于 \(n\),考虑是否能去掉这个 \(n\),我们统计每个格子的状态时,将 \(0/1/2/3\) 分开统计,四个状态各用一个 \(n\) 位二进制数表示每个格子是否存在该状态。

这样一来时间复杂度为 \(O(2^{\frac n2} (n^2 + m))\),足以通过。

点击查看代码
#include <bits/stdc++.h>
#define ll long long
#define ull unsigned ll
#define pir pair <ll, ll>
#define mkp make_pair
#define fi first
#define se second
#define pb push_back
using namespace std;
const ll maxn = 1010, mod = 1e9 + 7;
ll n, m, state[maxn][35], d[maxn], maxd, hh[1 << 16];
char str[maxn];
ll fas(ll x) {
	if((x & 3) == 3) return 1;
	if((x & 12) == 12) return 1;
	if((x & -x) == x) return 3;
	return 2;
}
ll _0[1 << 16], _1[1 << 16], _2[1 << 16], _3[1 << 16];
ll p0[1 << 16], p1[1 << 16], p2[1 << 16], p3[1 << 16];
void calc(ll q) {
	for(ll i = 0; i < 16; i++) {
		_0[1 << i] = p0[q] << i;
		_1[1 << i] = p1[q] << i;
		_2[1 << i] = (p2[q] << i) | ((1 << i) - 1);
		_3[1 << i] = p3[q] << i;
	}
	for(ll S = 1; S < (1 << 16); S++) {
		_0[S] = _0[S & (S - 1)] | _0[S & -S];
		_1[S] = _1[S & (S - 1)] | _1[S & -S];
		_2[S] = _2[S & (S - 1)] | _2[S & -S];
		_3[S] = _3[S & (S - 1)] | _3[S & -S];
	}
}
ll g(ll S, ll i) {
	return (((_3[S] >> i) & 1) << 3) | (((_2[S] >> i) & 1) << 2)
	| (((_1[S] >> i) & 1) << 1) | ((_0[S] >> i) & 1);
}
ll pw3[maxn], pw2[maxn];
namespace sub1 {
	ll prod[1 << 16], pf[1 << 16], id[maxn];
	ll solve() {
		ll ret = 0;
		for(ll S = 1; S < (1 << 16); S++) prod[S] = pf[S] = 1;
		for(ll i = 1; i <= m; i++) {
			calc(i); ll w = min(16ll, n - d[i]);
			for(ll S = 1; S < (1 << w); S++) {
				ll mask = (_0[S] & _1[S]) | (_2[S] & _3[S]);
				ll cc = (((1ll << n) - 1) ^ (_0[S] | _1[S])) |
				        (((1ll << n) - 1) ^ (_2[S] | _3[S]));
				cc &= ((1ll << n) - 1) ^ mask;
				ll x = __builtin_popcountll(cc), z = __builtin_popcountll(mask);
				ll tmp = pw3[x] * pw2[n - x - z] %mod;
				prod[S] = prod[S] * tmp %mod;
				if(d[i] < 16) pf[S] = pf[S] * tmp %mod;
			}
		}
		for(ll S = 1; S < (1 << 16); S++) {
			ll c = mod - 1;
			for(ll i = 0; i < 16; i++)
				if(S & (1 << i)) c = mod - c;
			ret = (ret + c * (prod[S] + mod - pf[S])) %mod;
		} return ret;
	}
}
namespace sub2 {
	ll dp[2][1 << 16][2], prod[1 << 16], prod_[1 << 16], rev[1 << 16];
	ll id[maxn];
	bool cmp(ll x, ll y) {return d[x] < d[y];}
	ll solve() {
		for(ll i = 1; i <= m; i++) id[i] = i;
		sort(id + 1, id + 1 + m, cmp);
		ll maxd = 0;
		for(ll i = 1; i <= m; i++)	
			if(d[i] < 16) maxd = max(maxd, d[i] + 1);
		if(maxd == 0) return 1;
		for(ll S = 0; S < (1 << 16); S++) {
			prod[S] = prod_[S] = 1;
			rev[S] = (rev[S >> 1] >> 1) | (S & 1? 1 << maxd - 1 : 0);
		} ll ret = 0;
		for(ll t = n - 1, r = 0; ~t; t--) {
			while(r < m && d[id[r + 1]] + t < n) {
				++r; if(d[id[r]] >= 16) continue;
				calc(id[r]);
				for(ll S = 0; S < (1 << maxd); S++) {
					ll x = hh[S & -S];
					prod[rev[S]] = prod[rev[S]] * fas(g(S, maxd - 1)) %mod;
					prod_[rev[S]] = prod_[rev[S]] * fas(g(S, maxd - 1) | 4) %mod;
				}
			}
			memset(dp[1], 0, sizeof dp[1]);
			dp[1][0][0] = mod - 1;
			for(ll i = 0; i < n; i++) {
				memset(dp[i & 1], 0, sizeof dp[i & 1]);
				for(ll S = 0; S < (1 << maxd - 1); S++) {
					for(ll z = 0; z < 2; z++) {
						ll T = S ^ (S & (maxd > 1? 1 << maxd - 2 : 0)), e = (S > T) | z;
						if(i ^ t)
							dp[i & 1][T << 1][e] = (dp[i & 1][T << 1][e] + dp[i & 1 ^ 1][S][z] * 
						                        (i < t || z? prod_[S << 1] : prod[S << 1])) %mod;
						if(i > t) continue;
						ll of = maxd > 1? T << 1|1 : 0;
						if(maxd == 1) e = 1;
						dp[i & 1][of][e] = (dp[i & 1][of][e] + mod - dp[i & 1 ^ 1][S][z] * 
						                      (i < t || z? prod_[S << 1|1] : prod[S << 1|1]) %mod) %mod;
					}
				}
//				for(ll S = 0; S < (1 << maxd - 1); S++)
//					printf("dp[%lld, %lld] = %lld, %lld\n", i, S, dp[i & 1][S][0], dp[i & 1][S][1]);
			}
			for(ll S = 0; S < (1 << maxd - 1); S++)
				ret = (ret + dp[n & 1 ^ 1][S][0] + dp[n & 1 ^ 1][S][1]) %mod; 
		}
		return ret;
	}
}
int main() {
	scanf("%lld%lld", &n, &m);
	pw2[0] = pw3[0] = 1;
	for(ll i = 1; i <= n; i++) {
		pw2[i] = pw2[i - 1] * 2 %mod;
		pw3[i] = pw3[i - 1] * 3 %mod;
	}
	for(ll i = 0; i < 16; i++) hh[1 << i] = i;
	for(ll i = 1; i <= m; i++) {
		scanf("%s", str + 1); ll len = strlen(str + 1);
		for(ll j = 0; j < n; j++) state[i][j] = 2;
		for(ll j = 1, x = 0; j <= len; j++) {
			if(str[j] == 'R') ++x;
			else if(str[j] == '*') state[i][x] ^= 1;
			else state[i][x] = str[j] - '0';
			d[i] = x;
		} maxd = max(maxd, d[i]);
		for(ll j = d[i] + 1; j < n; j++) state[i][j] = 2;
		for(ll j = 0; j < n; j++)
			if(state[i][j] == 0) p0[i] |= 1ll << j;
			else if(state[i][j] == 1) p1[i] |= 1ll << j;
			else if(state[i][j] == 2) p2[i] |= 1ll << j;
			else p3[i] |= 1ll << j;
			
	} ll ans = 0;
	ans += sub1::solve();
	ans += sub2::solve();
	printf("%lld", ans % mod);
	return 0;
}

标签:状态,Luogu7740,15,格子,ll,机器人,NOI2021,define
From: https://www.cnblogs.com/Sktn0089/p/18337659

相关文章

  • 无需编程打造交易机器人,助我力压华尔街专业投资者!
    作者:老余捞鱼原创不易,转载请标明出处及原作者。写在前面的话:    这篇文章部分编译摘录自外网投资大神“HenriqueCentieiro&BeeLee”的专栏(原文: UseThisChatGPTTradingBottoBeat99%ofWallStreetInvestors!)而我开发的AI顾投平台也学习了他们文中提......
  • Binance API:自动化机器人批量大小问题
    我正在尝试在Python上可用的BinanceAPI上构建自己的机器人。我目前正在尝试的是根据我的Binance账户中可用的金额/BTC来下订单购买/出售BTC。然后,代码应该将这笔现金转换为BTC并发出买入/卖出订单:iforder_book[-1]=="BUY":forbalanceinaccount_i......
  • 【FANUC】发那科机器人ROBOGUIDE安装教程(含安装包)
    ......
  • springboot+vue基于智能机器人的智能答疑系统的设计与实现【程序+论文+开题】-计算机
    系统程序文件列表开题报告内容研究背景随着人工智能技术的飞速发展,智能机器人已经渗透到我们生活的方方面面,从工业制造到家庭服务,再到教育领域,它们正逐步改变着人类的工作与学习方式。在教育领域,传统的教学模式面临着诸多挑战,如个性化教学不足、答疑效率低下等问题。在此背......
  • 电话机器人有哪些功能特点?
    首先,智能语音电话客服机器人应对客服场景中,第一是回答用户的问题,前期大量的客户因为可能遇到同样的问题,客服人员不断的重复回答客户问题,但是用了智能语音电话客服机器人就不一样了,智能语音电话客服机器人可以先解决客户简单重复的问题,如果智能语音电话客服机器人解决不了,最后人......
  • 智能电销机器人有效果吗?
    1是效率确实会提升很多,1个电销机器人1天的拨打量达到800~1200通2是电销机器人能用最适合的语音语调进行沟通,比如我们就遇到过有客户用非常温柔的女生,和非常有磁性的小哥哥的声音进行电销,如果接到好听的声音,相信也不会那么想一来就挂断电话吧!而且现在的电销机器人已经不是早两......
  • 金牌AI销售机器人,轻松直翻业绩!
    本文由ChatMoney团队出品老板们,你们现在的压力是不是越来越大了?员工和往年是一样的,但是业绩却比往年差一半!尤其是电商行业,是“优化”员工还是广招人才?人工成本都很高。还得把业绩拉上去?怎么办呢?目前我们采用Chatmoney全能知识库AI销售系统,用人工智能来实现降低人工成本,......
  • 利用DYNAMIXEL智能伺服舵机从《传送门2》中打造一个更优质的动画机器人小麦克利(Wheatl
    原文链接:https://www.youtube.com/watch?v=OEn9hZ-Tw1E   这段视频由ROBOTIS提供!大家好,我想给大家推荐一个精彩视频,在视频中展示了如何制作《传送门2》中的动画机器人小麦克利(Wheatley)。看看是如何利用DYNAMIXEL智能伺服系统让小麦克利活起来的。  对于那些可能想设......
  • ChatGPT:人工智能聊天机器人的工作原理详解
    ChatGPT:人工智能聊天机器人的工作原理详解在近年来的科技浪潮中,人工智能(AI)的飞速发展让我们见证了无数令人惊叹的成果。其中,ChatGPT作为一款先进的聊天机器人,凭借其出色的对话能力和广泛的应用场景,引起了广泛的关注。那么,ChatGPT是如何工作的呢?本文将为你揭开ChatGPT的神秘......
  • 【花雕学编程】Arduino FOC 之使用正逆运动学的二轴绘图机器人程序
    Arduino是一个开放源码的电子原型平台,它可以让你用简单的硬件和软件来创建各种互动的项目。Arduino的核心是一个微控制器板,它可以通过一系列的引脚来连接各种传感器、执行器、显示器等外部设备。Arduino的编程是基于C/C++语言的,你可以使用ArduinoIDE(集成开发环境)来编写、......