[CSP-S模拟测试]:位运算(数学)

题目传送门(内部题12)


输入格式

第一行,三个整数$n,m,c$,意义如上所述。
第二行,$n−1$个字符串,第$i$个字符串${opt}_i$代表第$i$个运算符。
第三行,$n$个整数,第$i$个整数$a_i$代表$x_i$的优美值。


输出格式

如果存在方案,输出$n$个整数,第$i$个整数代表$x_i$,整数之间用一个空格分隔。若存在多种方案,输出任意一种均可。如果不存在方案,输出$OvO$。


样例

样例输入1:

4 2 3
XOR AND OR
2 1 1 1

样例输出1:

3 2 1 2

样例输入2:

50 6 58
AND AND XOR AND AND OR AND XOR XOR AND XOR AND XOR OR XOR OR XOR OR OR XOR OR XOR XOR AND XOR OR AND AND XOR XOR AND XOR OR OR XOR XOR AND XOR XOR AND OR XOR XOR XOR AND XOR XOR XOR XOR
3 2 5 5 3 3 1 3 3 2 3 1 3 1 5 2 5 2 3 5 2 3 5 2 4 5 2 3 2 4 3 2 3 3 0 4 1 2 2 3 6 5 2 2 2 2 3 4 3 5

样例输出2:

7 3 61 62 7 7 16 19 28 48 7 8 7 8 31 48 47 48 35 31 10 21 62 12 57 62 3 11 12 15 56 5 38 35 0 60 32 3 20 56 63 47 48 48 48 36 7 60 56 61


数据范围与提示

样例1解释:

$3\ XOR\ 2\ AND\ 1\ OR\ 2=3$,且$x_i$的取值满足约束,是一组合法解。

数据范围:

对于所有数据,$1\leqslant n\leqslant {10}^5,0\leqslant a_i\leqslant m\leqslant 30$。


题解

悄悄观察一下数据范围,$n$很大,但是$m$只有$30$。

然后我们还能发现,其实和具体的值没有关系,只和二进制位下$1$的个数有关。

那么我们先来考虑有没有方案的问题,设$dp[i][j]$表示在前$i$个运算之后得到的运算结果中是否可能有$j$个$1$。

不妨设$X\ opt\ Y=S$,设$X,Y,S$的二进制位下分别有$,x,y,z$个$1$,设$X\& Y$下有$w$个$1$,那么分为一下三种情况:

  $\alpha.AND:s=w,w\leqslant x,w\leqslant y,x+y−w\leqslant m$

  $\beta.OR:s=x+y−w,w\leqslant x,w\leqslant y,x+y−w\leqslant m$

  $\gamma.XOR:s=x+y−2w,w\leqslant x,w\leqslant y,x+y−w\leqslant m$

那么我们只需要看$dp[n][c]$是否为$1$即可。

下面来想方案可行的情况(毕竟数据点中没有$OvO$),设$pre[i][j]$表示在前$i$个运算之后得到的运算结果中有$j$个$1$是由前$i-1$个运算之后有多少个$1$转移过来的,那么你可能会问,可能有好多种情况可以转移过来,那么怎么办?其实,我们只需要一种情况,所以我们只需要记录一种转移即可。

那么如何计算答案呢?

我们不妨使用$BFS$,传两个参$pos$表示当前计算的位置,$state$表示当前计算之后的结果。

为方便,设:$pre\ opt\ now=state$。

那么我们现在知道了这一步计算的结果,上一部计算的结果中有几个$1$(暂设为$y_i$)和$x_i$,看起来不够,但是硬着头皮往下推会发现其实绰绰有余。

  $\alpha.AND:$我们可以这样想,先使$pre$和$now$凑出$state$中所有的$1$,然后多出来的不要重复就好啦,实现起来也非常简单。

  $\beta.OR:$我的思路就是,$pre$从前枚举所有$state$的$1$,$now$从后往前枚举所有$state$的$1$,为什么这样是正确的呢?因为我们走到这一步,已经保证了答案的合法,所以这么肆无忌惮的操作一定是可行的。

  $\gamma.XOR:$依然是枚举$state$中的$1$,然后看$x_i$和$y_i$谁更大,用大的去填$state$中的$1$,填完之后只要选一样的$1$即可,那么你还可能会问,如果填完之后$x_i$和$y_i$不一样怎么办呢?还是上面的那句话,路都走到这儿了,结果肯定合法。

我们就这样,一步一步的往前找就能找到答案啦,然后倒序输出即可。

时间复杂度:$\Theta(n\times m^2)$。

期望得分:$100$分。

实际得分:$100$分。


代码时刻

#include<bits/extc++.h>
using namespace std;
int n,m,c,x;
int pre[200001][50];
int cz[200001],a[200001];
bitset<50> bit;
bool vis[50];
void getans(int pos,int state)
{
	if(pos==1){printf("%d ",state);return;}
	bit=state;
	int gs=bit.count();
	int x=pre[pos][gs];
	int y=a[pos];
	int pr=0;
	int nw=0;
	memset(vis,0,sizeof(vis));
	switch(cz[pos])
	{
		case 1:
			for(int i=0;i<m;i++)if(state&(1<<i)){vis[i]=1;x--;y--;pr|=(1<<i);nw|=(1<<i);}
			for(int i=0;i<m&&x;i++)if(!vis[i]){x--;pr|=(1<<i);vis[i]=1;}
			for(int i=0;i<m&&y;i++)if(!vis[i]){y--;nw|=(1<<i);}
			break;
		case 2:
			for(int i=0;i<m&&x;i++)if(state&(1<<i)){x--;pr|=(1<<i);}
			for(int i=m-1;i>=0&&y;i--)if(state&(1<<i)){y--;nw|=(1<<i);}
			break;
		case 3:
			for(int i=0;i<m;i++)if(state&(1<<i)){if(x>y){x--;vis[i]=1;pr|=(1<<i);}else{y--;vis[i]=1;nw|=(1<<i);}}
			for(int i=0;i<m&&x&&y;i++)if(!vis[i]){x--;y--;pr|=(1<<i);nw|=(1<<i);}
	}
	getans(pos-1,pr);
	printf("%d ",nw);
}
int main()
{
	scanf("%d%d%d",&n,&m,&c);
	bit=c;
	x=bit.count();
	for(int i=2;i<=n;i++)
	{
		char opt[5];
		scanf("%s",opt+1);
		switch(opt[1])
		{
			case 'A':cz[i]=1;break;
			case 'O':cz[i]=2;break;
			case 'X':cz[i]=3;break;
		}
	}
	for(int i=1;i<=n;i++)
		scanf("%d",&a[i]);
	memset(pre,-1,sizeof(pre));
	pre[1][a[1]]=0;
	for(int i=2;i<=n;i++)
		for(int j=0;j<=m;j++)
			if(pre[i-1][j]!=-1)
				switch(cz[i])
				{
					case 1:for(int k=max(a[i]+j-m,0);k<=min(a[i],j);k++)pre[i][k]=j;break;
					case 2:for(int k=max(a[i],j);k<=min(a[i]+j,m);k++)pre[i][k]=j;break;
					case 3:for(int k=abs(j-a[i]);k<=min(j+a[i],2*m-j-a[i]);k+=2)pre[i][k]=j;break;
				}
	getans(n,c);
	return 0;
}

rp++

转载于:https://www.cnblogs.com/wzc521/p/11370302.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值