• [NOI2021] 机器人游戏


    一、题目

    点此看题

    二、解法

    全程在 \(\tt Cirno\_9\) 的帮助下补了这道题,果然他又帅又强

    根据样例解释的提示,考虑对起点容斥。也就是枚举一个起点集合 \(S\),求有多少种输入使得从 \(S\) 中的起点出发,得到的输出相同,然后带上 \((-1)^{|S|+1}\) 的容斥系数即可。

    起点对位置 \(i\) 的限制只有 \(4\) 种,即 赋值0/赋值1/不变/取反,可以直接分类讨论计算方案数:

    • 如果走出去了,那么整个序列的格子贡献都是 \(1\)
    • 如果同时拥有前两种或者后两种,那么 \(i\) 的贡献为 \(1\)
    • 如果前两种有一个,后两种有一个,那么 \(i\) 的贡献为 \(2\)
    • 如果只拥有四种中的一种,那么 \(i\) 的贡献为 \(3\)

    暴力进行这个过程,对于每个机器人分开来计算,再用乘法原理即可得到答案,时间复杂度 \(O(n^2m2^n)\sim O(nm2^n)\)

    考虑优化,主要矛盾在于 \(2^{n}\),我们想办法用折半的方法优化它,枚举最后的起点 \(p\),有这样的 \(\tt observation\)

    • \(p\leq \frac{n}{2}\),可以直接暴力枚举 \(S\),时间复杂度 \(O(nm2^{n/2})\)
    • \(p>\frac{n}{2}\),那么需要考虑的序列必然满足步数 \(s<n-p+1\)

    对于第二种情况我们考虑 \(dp\),设 \(f(i,S,0/1)\) 表示现在考虑到了格子 \(i\),和格子 \(i\) 距离 \(n-p\) 以内的选取情况是 \(S\),在这个距离以外有无被选取的点(因为这会新增一个不变的限制),那么状态数是 \(O(n2^{n/2})\) 的。转移考虑枚举当前格子是否作为起点被选取,如果被选取会增加 \(-1\) 的容斥系数(即 \(dp\) 计算容斥系数的技巧)

    时间复杂度 \(O(nm2^{n/2})\),现在的瓶颈在于 \(m\),考虑不同序列的 \(dp\) 转移方式是完全相同的,而且他们又独立所以支持乘法原理。所以考虑整体 \(dp\),转移时一次性考虑 \(m\) 个格子,我们需要现在需要做的是快速计算贡献。

    开四个 bitset 维护每个序列的四种限制是否存在,那么只需要位运算就可以计算贡献。考虑如何处理出这些 bitset,在暴力枚举 \(S\) 时,我们在线处理 \(t_S\) 表示起点集合为 \(S\)bitset;在 \(dp\) 转移时,我们预处理 \(t_S\) 表示距离集合为 \(S\) 时的 bitset,然后就可以得到转移系数。

    时间复杂度 \(O(\frac{nm2^{n/2}}{w})\)

    #include <cstdio>
    #include <bitset>
    #include <cstring>
    #include <iostream>
    using namespace std;
    const int M = 1005;
    const int N = 1<<16;
    #define int long long
    const int MOD = 1e9+7;
    int read()
    {
    	int x=0,f=1;char c;
    	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
    	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
    	return x*f;
    }
    int n,m,k,a[N],b[N],p2[M],p3[M];
    int ans,f[N][2],g[N][2],dp[N],lg[N];
    bitset<M> R,s1,s2,s3,in[M];
    struct node
    {
    	bitset<M> b[4];
    	node operator | (const node &v) const
    	{
    		node r;
    		for(int i=0;i<4;i++) r.b[i]=b[i]|v.b[i];
    		return r;
    	}
    	void set(int x,int y) {b[x].set(y);}
    	int val()
    	{
    		s3=R&~((b[0]&b[1])|(b[2]&b[3]));
    		s1=s3&(b[0]^b[1]);s2=s3&(b[2]^b[3]);
    		int x=s1.count()+s2.count(),y=(s1&s2).count();
    		return p2[y]*p3[x-2*y]%MOD;
    	}
    }h[M],t[N],Z;
    void add(int &x,int y) {x=(x+y)%MOD;}
    void work(int p)
    {
    	R=in[p];int L=1<<(n-p+1);
    	memset(f,0,sizeof 0);f[0][0]=-1;
    	for(int i=0;i<L;i++)
    	{
    		a[i]=t[i].val();
    		node c9=t[i];c9.b[2].set();
    		b[i]=c9.val();//sad
    	}
    	for(int j=1;j<=n;j++)
    	{
    		for(int i=0;i<L;i++) g[i][0]=g[i][1]=0;
    		for(int i=0;i<L;i++)
    		{
    			int k=i<<1,w=k&(L-1),o=(k!=w),*z=(o?b:a);
    			if(j!=p)//not choose
    			{
    				if(j<p) add(g[w][o],f[i][0]*b[w]);
    				else add(g[w][o],f[i][0]*z[w]);
    				add(g[w][1],f[i][1]*b[w]);
    			}
    			if(j<=p)//choose
    			{
    				w|=1;
    				if(j<p) add(g[w][o],-f[i][0]*b[w]);
    				else add(g[w][o],-f[i][0]*z[w]);
    				add(g[w][1],-f[i][1]*b[w]);
    			}
    		}
    		swap(f,g);
    	}
    	for(int i=0;i<L;i++)
    		add(ans,f[i][0]+f[i][1]);
    }
    signed main()
    {
    	n=read();m=read();k=n/2;
    	p2[0]=p3[0]=1;
    	for(int i=1;i<=m;i++)
    	{
    		static char s[M]={};
    		scanf("%s",s+1);Z.set(2,i);
    		int w=2,len=0,t=strlen(s+1);
    		p2[i]=p2[i-1]*2%MOD;
    		p3[i]=p3[i-1]*3%MOD;
    		for(int j=1;j<=t;j++)
    		{
    			if(s[j]=='R') h[len++].set(w,i),w=2;
    			else if(s[j]=='*') w^=1;
    			else w=s[j]-'0';
    		}
    		h[len].set(w,i);
    		for(int j=1;j<=n;j++)
    			if(len+j<=n) in[j].set(i);
    		for(int j=len+1;j<=n;j++) h[j].set(2,i);
    	}
    	dp[0]=lg[0]=-1;
    	for(int i=1;i<N;i++) lg[i]=lg[i>>1]+1;
    	for(int i=1;i<(1<<k);i++) dp[i]=-dp[i&(i-1)];
    	//p<=n/2
    	for(int i=1;i<=n;i++)
    	{
    		for(int s=1;s<(1<<k);s++)
    		{
    			int d=i-lg[s&(-s)]-1;
    			t[s]=t[s&(s-1)]|(d<0?Z:h[d]);
    			R=in[lg[s]+1];
    			dp[s]=dp[s]*t[s].val()%MOD;
    		}
    	}
    	for(int s=1;s<(1<<k);s++) add(ans,dp[s]);
    	//p>n/2
    	for(int s=1;s<N;s++)
    		t[s]=t[s&(s-1)]|h[lg[s&(-s)]];
    	for(int i=k+1;i<=n;i++) work(i);
    	printf("%lld\n",(ans+MOD)%MOD);
    }
    
  • 相关阅读:
    Spring MVC注解中@PathVariable和@RequestParam的区别
    Spring MVC请求流程
    eclipse-web项目目录结构
    数论基础------质数板
    线性DP基础--acwing---动态规划
    背包基础
    ----------动态规划分界线----------
    2020牛客暑期多校训练营(第三场)
    区间选点-贪心-acwing
    2020牛客暑期多校训练营(第二场)
  • 原文地址:https://www.cnblogs.com/C202044zxy/p/16523730.html
Copyright © 2020-2023  润新知