DP+乱搞 位运算

45 篇文章 0 订阅
36 篇文章 0 订阅

今天的题都这么。。。。
这道题后面会用到逆推。。。。
首先得判断是否有合法方案。而且方案明显会有很多种,但只需要统计算到这一位时答案有多少个1。
设f[i][j]表示算完i位时,答案里有j个1.
考虑转移,转移时对答案产生影响的还有两位间1位置的交集,也就是f[i][j]&a[i+1]后1的个数,设它为k。
交集中1个数就是
运算符是 & : k
运算符是 |:j+a[i+1]-k
运算符是 ^:j-a[i+1]-k*2
想想都明白。。转移过程中再维护一下[i+1][k]是从哪里转移过来的,说白了就是g[i+1][k]=j..
设C为c中1的个数,如果f[n][C]==1那就一定能找出一种方案,==0一定无解(其实测试点里完全没有。。。)
剩下的就是考虑倒推,把答案推出来了。设ans[i]为第i步运算完的答案,w[i]为第i位上的值(就是最后输出的结果)。那么当前知道ans[n]=c,要推出ans[n-1]和w[i]。依次类推出所有的。因为题目说只用输出一组解,那么怎么方便怎么搞就行。(实际上spj完全不能用。。。结果各种猜出题人意图,才输出了测试点。。。)
还是分别考虑。对了,首先要有ans[i-1],w[i],还有ans[i-1]&w[i]中1的个数,当前有a[i],g[i-1][last],还有last的,把上面的式子移一下项k就出来了。
&:ans[i]含有的1,ans[i-1]和w[i]里都有,所以直接赋过去,然后考虑ans[i-1]和w[i]还有1没被填上,先满足ansi-1,找到ans[i]是0的位,赋成1,再满足w[i],找ans[i-1],ans[i]都是0的位就好了。
|:ans[i]是ans[i-1]和w[i]的并集,所以所有的1都在ans[i]是1的位上,模拟运算过程搞。先满足交际,再分别找各自为1的位置。
^:这个ans[i-1]和w[i]的交集的位上ans[i]都是0,而它们分别为1的位ans[i]为1.
根据这个倒推到头,w[1]=ans[1],就好了。
博主我想揍死出题人。

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 100005
using namespace std;
int read()
{
   int sum=0,f=1;char x=getchar();
   while(x<'0'||x>'9'){if(x=='-')f=-1;x=getchar();}
   while(x>='0'&&x<='9'){sum=(sum<<3)+(sum<<1)+x-'0';x=getchar();}
   return sum*f;
}
int n,m,c,C,xp[32],a[N],opt[N],g[N][32],ans[N],w[N];
bool f[N][32];
void dfs(int x,int las)
{
    if(x==1)
    {
        w[1]=ans[1];
        for(int i=1;i<=n;i++)printf("%d ",w[i]);
        exit(0);
    }
    int s=g[x][las],ax=a[x],l;
    if(opt[x-1]==1)
    {
        l=las;ans[x-1]=w[x]=ans[x];ax-=l;s-=l;
        for(int i=0;i<m;i++)
            if((ans[x]&xp[i])==0&&s)ans[x-1]+=xp[i],s--;
            else if((ans[x]&xp[i])==0&&ax)w[x]+=xp[i],ax--;
    }
    if(opt[x-1]==2)
    {
        l=s+ax-las;s-=l;ax-=l;
        for(int i=0;i<m;i++)
            if(ans[x]&xp[i]&&l)w[x]+=xp[i],ans[x-1]+=xp[i],l--;
            else if((ans[x]&xp[i])&&s)ans[x-1]+=xp[i],s--;
            else if((ans[x]&xp[i])&&(ans[x-1]&xp[i])==0&&ax)w[x]+=xp[i],ax--;
    }
    if(opt[x-1]==3)
    {
        l=(s+ax-las)/2;s-=l;ax-=l;
        for(int i=0;i<m;i++)
            if((ans[x]&xp[i])==0&&l)w[x]+=xp[i],ans[x-1]+=xp[i],l--;
            else if((ans[x]&xp[i])&&s)ans[x-1]+=xp[i],s--;
            else if((ans[x]&xp[i])&&(ans[x-1]&xp[i])==0&&ax)w[x]+=xp[i],ax--;
    }
    dfs(x-1,g[x][las]);
}
int main()
{
    n=read();m=read();c=read();xp[0]=1;
    for(int i=1;i<=m;i++)xp[i]=xp[i-1]*2;
    char s[5];
    for(int i=1;i<n;i++)
    {
        scanf("%s",s);
        if(s[0]=='A')opt[i]=1;
        if(s[0]=='O')opt[i]=2;
        if(s[0]=='X')opt[i]=3;
    }
    for(int i=1;i<=n;i++)a[i]=read();
    f[1][a[1]]=1;
    for(int i=1;i<n;i++)
        for(int j=0;j<=m;j++)
        {   
            if(!f[i][j])continue;
            for(int k=0;k<=min(a[i+1],j);k++)
               if(j+a[i+1]-k<=m)
                    if(opt[i]==1)f[i+1][k]=1,g[i+1][k]=j;
                    else if(opt[i]==2)f[i+1][j+a[i+1]-k]=1,g[i+1][j+a[i+1]-k]=j;
                    else if(opt[i]==3)f[i+1][j+a[i+1]-k*2]=1,g[i+1][j+a[i+1]-k*2]=j;
        }
    for(int i=0;i<m;i++)
       if(xp[i]&c)C++;
    if(!f[n][C]){printf("OvO\n");return 0;}
    ans[n]=c;
    dfs(n,C);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值