范围n-----$100000$   m $30$

输出方案

这是一个很好的$dp$题

首先我们应该看出来一条性质只要你最后有方案达到$n$个$1$,那么你可以达到任何`一种$n$个$1$的情况

例如

你最后可以达到$3$个$1$

那么你可以达到$11100 $  $ 01110$    $01011$    $01101$等方案

证明:题目里说的操作和优美值都是一的个数相关,只要我们可以达到$n$个一,我们可以通过变换$1$的位置得到多种方案

于是题目里给的$C$我们并不关心,我们只关心$1$的个数,我们把1都放到最右面,另外这也给我们下面dp提供方便

我们先看各个运算意义

假设你$x$和$y$中有$s$个重复的$1$求各个运算之后一的个数,

$\&$后$1$的个数就是$s$,

对于$|$我们如果都加的话重复的多加一次

$|$后$1$的个数就是$x+y-s$

重复变为0

^后$1$的个数就是$x+y-s-s$

然后我们可以尝试列一个$dp$

设$f[x][j]$表示第$x$个操作有$j$个$1$的情况,我们当有这种情况就设为$1$,设最后$C$中一的个数为$x$,我们只需要看$f[n][x]$,若$f[n][x]$为$1$递归找解(后面会说到,其实递归找解是这个题最难的地方),若为$0$就是无解

根据上面得出结论我们可以列出

枚举这次操作后重复个数为w,设这次优美值为$G[i]$,枚举之前1的个数为$j$

若这次操作为$\&$则$f[x][w]=max(f[x-1][j],f[x][w])$

若为$|$ 则$f[x][G[i]+j-w]=max(f[x][G[i]+j-w],f[x-1][j])$

类似的若为^则$f[x][G[i]+j-w*2]=max(f[x][G[i]+j-w*2],f[x-1][j])$

注意一下边界以及循环枚举

            for(ll w=max(0ll,G[i]+j-m);w<=min(j,G[i]);w++)

看这句max,因为当它比m大时一定有重叠部分,且重叠部分至少为$G[i]+j-m$

然后就到了这个题难点记录方案

思考我们已知条件

我们从后往前搜,我们目前知道的是最终值,我们每一次都把前一位的值算出来,再搜前一位

那么我们已知运算符号已知运算前后1的个数,和运算后的值,我们现在要做的就是求出前一位值,且我们知道这一定能找到一组合法解

三个运算符号分开考虑,

假设符号为$|$,我们已知当前1个数(G[i]),和这次操作之后1的个数,这次操作之前1的个数

我们只需要让这次操作之前数,这次操作数二进制下1完美覆盖掉这次操作1的个数即可

因为我们符号$|$,我们只需要一个从前扫,一个从后扫,类似ST重复无所谓

例如操作前$3$个$1$,这次操作$3个1$操作后$11011$,我们一个从前扫得到$11010$另一个得到$01011$

假设符号为$\&$,&只会让数值变小,我们可以断定操作前1的个数和这次操作1一定>=操作后

我们先保证这些操作后上有值的位一定有$1$,然后我们分开放剩下的$1$

例如操作前$4$个$1$,这次操作$3$个$1$,操作后$10001$,我们先得到$10001$   $10001$,再分开放1得到$11101$和$10011$

假设我们当前符号^,思考^运行他会使相同的位置变成0,不同变为1

那么我们重复的地方可以算出是(之前1的个数+这次1的个数-操作后1的个数)/2

然后先给不重复地方赋值,再分别给重复地方赋1

例如操作前$4$个$1$,这次操作$2$个$1$,操作后$11101$,我们先得到重复长度1,然后得到$11100$和$00001$,最后给重复赋值$11110$,$00011$

void dfs(ll pos,ll now)
{if(pos==1){printf("%lld ",now);return ;}ll cnt_1=0;
//    printf("pos=%lld\n",pos);for(ll i=1;i<=m;i++)if(now&(1<<(i-1)))cnt_1++;if(ch[pos][1]=='A'){ll nowk=now,tot=cnt_1,pr=pre[pos][cnt_1],nxtk=now;//知道目前的数,知道目前cnt,知道之前cnt,求之前数//因为是&所以在满足k条件下尽量差开,分开放最优//例如10001//先放11001再放10111for(ll i=1;i<=m;i++){if(tot==G[pos]) break;if(!(now&(1<<(i-1))))nowk|=(1<<(i-1)),tot++;}tot=cnt_1;for(ll i=1;i<=m;i++){if(tot==pr) break;if(!(nowk&(1<<(i-1))))nxtk|=(1<<(i-1)),tot++;}
//        printf("nxtk=%lld nowk=%lld\n",nxtk,nowk);dfs(pos-1,nxtk);printf("%lld ",nowk);}if(ch[pos][1]=='O'){ll nowk=0,tot=0,pr=pre[pos][cnt_1],nxtk=0;//知道目前的数,知道目前cnt,知道之前cnt,求之前数//那么向kmp一样或无所谓for(ll i=1;i<=m;i++){if(tot==G[pos])break;if(now&(1<<(i-1)))nowk|=(1<<(i-1)),tot++;}tot=0;for(ll i=m;i>=1;i--){if(tot==pr)break;if(now&(1<<(i-1)))nxtk|=(1<<(i-1)),tot++;}
//        printf("nx=%lld no=%lld\n",nxtk,nowk);dfs(pos-1,nxtk);printf("%lld ",nowk);}if(ch[pos][1]=='X'){ll nowk=0,tot=0,pr=pre[pos][cnt_1],nxtk=0,lst=0;ll chong=(pr+G[pos]-cnt_1)/2;//计算出重合部分,重合部分就是第一个长度+第二个长度-总1数/2for(ll i=1;i<=m;i++){if(tot==G[pos]-chong)break;if(now&(1<<(i-1)))nowk|=(1<<(i-1)),tot++,lst=i;}tot=0;for(ll i=m;i>=lst+1;i--){if(tot==pr-chong)break;if(now&(1<<(i-1)))nxtk|=(1<<(i-1)),tot++;}tot=0;for(ll i=1;i<=m;i++){if(tot==chong)break;if(!(now&(1<<(i-1))))nowk|=(1<<(i-1)),nxtk|=(1<<(i-1)),tot++;}
//        printf("nxk=%lld nok=%lld\n",nxtk,nowk);dfs(pos-1,nxtk);printf("%lld ",nowk);}
}

求方案代码

总代码

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define A 1010101
ll f[A][32],pre[A][32],G[A],dl[A];
ll x,n,m,c;
char ch[A][6];
bitset<36> b;
void dfs(ll pos,ll now)
{if(pos==1){printf("%lld ",now);return ;}ll cnt_1=0;
//    printf("pos=%lld\n",pos);for(ll i=1;i<=m;i++)if(now&(1<<(i-1)))cnt_1++;if(ch[pos][1]=='A'){ll nowk=now,tot=cnt_1,pr=pre[pos][cnt_1],nxtk=now;//知道目前的数,知道目前cnt,知道之前cnt,求之前数//因为是&所以在满足k条件下尽量差开,分开放最优//例如10001//先放11001再放10111for(ll i=1;i<=m;i++){if(tot==G[pos]) break;if(!(now&(1<<(i-1))))nowk|=(1<<(i-1)),tot++;}tot=cnt_1;for(ll i=1;i<=m;i++){if(tot==pr) break;if(!(nowk&(1<<(i-1))))nxtk|=(1<<(i-1)),tot++;}
//        printf("nxtk=%lld nowk=%lld\n",nxtk,nowk);dfs(pos-1,nxtk);printf("%lld ",nowk);}if(ch[pos][1]=='O'){ll nowk=0,tot=0,pr=pre[pos][cnt_1],nxtk=0;//知道目前的数,知道目前cnt,知道之前cnt,求之前数//那么向kmp一样或无所谓for(ll i=1;i<=m;i++){if(tot==G[pos])break;if(now&(1<<(i-1)))nowk|=(1<<(i-1)),tot++;}tot=0;for(ll i=m;i>=1;i--){if(tot==pr)break;if(now&(1<<(i-1)))nxtk|=(1<<(i-1)),tot++;}
//        printf("nx=%lld no=%lld\n",nxtk,nowk);dfs(pos-1,nxtk);printf("%lld ",nowk);}if(ch[pos][1]=='X'){ll nowk=0,tot=0,pr=pre[pos][cnt_1],nxtk=0,lst=0;ll chong=(pr+G[pos]-cnt_1)/2;//计算出重合部分,重合部分就是第一个长度+第二个长度-总1数/2for(ll i=1;i<=m;i++){if(tot==G[pos]-chong)break;if(now&(1<<(i-1)))nowk|=(1<<(i-1)),tot++,lst=i;}tot=0;for(ll i=m;i>=lst+1;i--){if(tot==pr-chong)break;if(now&(1<<(i-1)))nxtk|=(1<<(i-1)),tot++;}tot=0;for(ll i=1;i<=m;i++){if(tot==chong)break;if(!(now&(1<<(i-1))))nowk|=(1<<(i-1)),nxtk|=(1<<(i-1)),tot++;}
//        printf("nxk=%lld nok=%lld\n",nxtk,nowk);dfs(pos-1,nxtk);printf("%lld ",nowk);}
}
int main(){scanf("%lld%lld%lld",&n,&m,&c);for(ll i=2;i<=n;i++){scanf("%s",ch[i]+1);}b|=c;x=b.count();
//    printf("x=%lld\n",x);for(ll i=1;i<=n;i++){scanf("%lld",&G[i]);}f[1][G[1]]=1;for(ll i=2;i<=n;i++)for(ll j=0;j<=m;j++){if(!f[i-1][j]) continue;for(ll w=max(0ll,G[i]+j-m);w<=min(j,G[i]);w++){if(ch[i][1]=='A'){if(f[i-1][j]==1)f[i][w]=1,pre[i][w]=j/*,printf("A pos=%lld pre[%lld][%lld]=%lld\n",i,i,w,pre[i][w])*/;}if(ch[i][1]=='O'){if(f[i-1][j]==1)f[i][j+G[i]-w]=1,pre[i][j+G[i]-w]=j/*,printf("O pos=%lld pre[%lld][%lld]=%lld\n",i,i,j+G[i]-w,pre[i][j+G[i]-w])*/;}if(ch[i][1]=='X'){if(f[i-1][j]==1)f[i][j+G[i]-2*w]=1,pre[i][j+G[i]-2*w]=j/*,printf("X pos=%lld pre[%lld][%lld]=%lld\n",i,i,j+G[i]-2*w,pre[i][j+G[i]-2*w])*/;}}}dfs(n,c);
}

View Code

转载于:https://www.cnblogs.com/znsbc-13/p/11361565.html

NOIP模拟测试22「位运算」相关推荐

  1. NOIP模拟测试8「匹配·回家」

    匹配 哈希能A 水到爆炸 回家 事实上我做过一个原题,甚至比这个回家难的多,而且那个题多组询问必经点 然后我做一组询问就打炸了 大约就是删了很多东西,然后自己想的太简单了 直接统计了割点,懒得打lca ...

  2. NOIP模拟测试19「count·dinner·chess」

    反思: 我考得最炸的一次 怎么说呢?简单的两个题0分,稍难(我还不敢说难,肯定又有人喷我)42分 前10分钟看T1,不会,觉得不可做,完全不可做,把它跳了 最后10分钟看T1,发现一个有点用的性质,仍 ...

  3. NOIP模拟测试30「return·one·magic」

    magic 题解 首先原式指数肯定会爆$long$ $long$ 首先根据欧拉定理我们可以将原式换成$N^{\sum\limits_{i=1}^{i<=N} [gcd(i,N)==1] C_{G ...

  4. NOIP模拟测试38「金·斯诺·赤」

    金 辗转相减见祖宗 高精 #include<bits/stdc++.h> using namespace std; #define A 2000 #define P 1 #define N ...

  5. NOIP模拟测试21「折纸·不等式」

    折纸 题解 考试时无限接近正解,然而最终也只是接近而已了 考虑模拟会爆炸,拿手折纸条试一试,很简单 考你动手能力 代码 #include<bits/stdc++.h> using name ...

  6. NOIP模拟测试18「引子·可爱宝贝精灵·相互再归的鹅妈妈」

    待补 引子 题解 大模拟,注意细节 代码1 #include<bits/stdc++.h> using namespace std; int n,m;char a[1005][1005]; ...

  7. NOIP模拟测试13「矩阵游戏·跳房子·优美序列」

    矩阵游戏 考试时思路一度和正解一样,考试到最后还是打了80分思路,结果80分打炸了只得了40分暴力分 题解 算出来第一列的总值,每次通过加每两列之间的差值得出下一列的总值 算第一列我们只需要让当前点* ...

  8. NOIP模拟测试10「大佬·辣鸡·模板」

    大佬 显然假期望 我奇思妙想出了一个式子$f[i]=f[i-1]+\sum\limits_{j=1}^{j<=m} C_{k \times j}^{k}\times w[j]$ 然后一想不对得容 ...

  9. NOIP模拟测试7「方程的解·visit」

    visit 由于一些不可预知的错误导致我一直WA 错误最后说 思路 方案一 假设终点在出发点右上方(这样假设只是为了方便) 假设向左走了a步,向右下了b布,那么相应的我们要向右走m+a,向上n+b步 ...

最新文章

  1. mysql多索引结构_MySQL 索引结构
  2. C++矩阵运算库推荐
  3. Debian 安装docker
  4. 在VC中如何找到崩溃的源头(二)
  5. 1036 跟奥巴马一起编程 (15分)——16行代码AC
  6. LeetCode Permutations
  7. Java架构师必须知道的 6 大设计原则
  8. Linux系统编程---5(共享存储映射,存储映射I/O,mmap函数,父子进程间通信,匿名映射)
  9. C++派生类的构造函数和析构函数
  10. 数据分析报告应该包含的内容
  11. 如何将日志系统切换到 Logback?
  12. linux安装vmwareTools
  13. mysql 数据库基础教程(一)
  14. linux glibc升级
  15. 阿里G6可视化双向箭头实现
  16. 奇计淫巧______bitset优化
  17. 2019 NLP大全:论文、博客、教程、工程进展全梳理(长文预警)
  18. [思维][模拟]Scholomance Academy 第45届icpc区域赛沈阳站K
  19. Unity 攻击范围检测
  20. kernel启动流程-start_kernel的执行_8.cpio initrd解包

热门文章

  1. python if else用法_python列表推导式中使用if-else
  2. jsp中out.println()报红
  3. Spring Cloud Feign 负载均衡
  4. 驾校约车html网站源码,html5首汽约车微信感恩活动页面模板
  5. python find函数实现原理_非常干货:Python 探针实现原理
  6. LeetCode 15 二进制中1的个数
  7. html 怎么置顶表格,表格(Table)表头固定,内容上滚【5个实例】
  8. mysql的复制订阅_如何删除发布与复制订阅数据库 'distribuion' 的方法
  9. android 获取当前网络,Android 获取当前网络连接的类型信息
  10. java 判断对象是否是xml格式_java对象与xml格式之间的转换