[Tkey] 与非
解法原理1
首先我们需要明白 \(\operatorname{nand}\) 的运算:
\]
这个很好理解,因为 \(\operatorname{nand}\) 就是这么定义的(从中文名字可以看出来)。
\]
这个是因为下述式子:
\]
最后:
\]
因为 \(\operatorname{and},\operatorname{or}\) 我们都表示出来了,因此我们还可以用 \(\operatorname{nand}\) 来表示 \(\operatorname{xor}\)。
所以可以发现 \(\operatorname{nand}\) 实际上包含了全部我们需要的位运算。
解法原理2
拥有了全部的位运算,那么现在我们是不是就能得到所有数字了呢?显然不是,考虑一下的例子:
10101
00000
可以发现我们无论如何操作,都不能使第 \(2\) 位和第 \(4\) 位变成不同的数字,这就是我们这道题唯一的限制:假如在每个数 \(n\) 中都有数位 \(i,j\) 是相同的,那么无论如何选择,在结果中一定有 \(i=j\)。
回到刚才的例子,可以发现第 \(1,3,5\) 位也无法在结果中变成不同的数字,发现其实不一定要全部相等,而是每个数中的各位相等即可,因此归纳出本题的限制条件:
若在全部的 \(n\) 个数中都有第 \(i\) 位与第 \(j\) 位相等,那么在结果中也会如此。
因此,本题首先需要我们求解出具有限制条件的数位,我们可以使用并查集来维护。
代码步骤1
因为本题 \(n,k\) 较小,可以考虑暴力枚举进行合并。
我们每次枚举两个位置 \(i,j\) 判断这两个位置是否对全部的 \(n\) 个数都满足上述条件,如果是,那么我们就合并这两个数。最后合并到一起的几个数就是符合条件,并且范围最大的结果。
请注意此处并查集合并操作的合并顺序,具体用处请见解法原理3的流程第一条。
//主函数部分
for(int i=1;i<=k;++i){
fa[i]=i;//初始化
}
for(int i=1;i<=k;++i){
for(int j=i-1;j>0;--j){
//枚举全部可能的 i,j
if(check(i,j)) connect(i,j);
}
}
//并查集板子与 check()
// ---DSU Part---
int fa[61];
int find(int id){
if(fa[id]==id) return id;
fa[id]=find(fa[id]);
return fa[id];
}
void connect(int x,int y){
if(find(x)!=find(y)){
fa[fa[y]]=fa[x];
}
}
// --------------
// Check
bool check(int x,int y){
//检查 x,y 是否符合条件
for(int i=1;i<=n;++i){
if(((a[i]>>x-1)^(a[i]>>y-1))&1)
//上面这句的意思是:判断 a[i] 的第 x,y 位是否相等
return false;
}
return true;
}
解法原理3
求出全部“相互独立的数”之后,我们现在需要求解答案了。
首先考虑统计出当前并查集中的集合个数 \(a\),这 \(a\) 个集合相互独立,互不影响,并且每个集合都可以独立地拼凑出 \(1\) 或 \(0\)(刚才的解法原理1证明了,这样的拼凑总是可能的)。因此方案数应该为 \(2^{a}\)。
现在我们来考虑边界问题。我们可以很容易地将边界 \([l,r]\) 转化成求 \([1,r]-[1,l-1]\),这样我们就只需要处理右边界了。下面我们可以把问题转化成一个数位 DP。
假如右边界 \(x\) 的第 \(i\) 位数为 \(0\),说明这一位可能受到了限制(相当于数位 DP 里的 \(limit\)),例如 x=010010
,其右数第 \(3\) 位为 \(0\),这说明在前几位填 010
的时候,这一位实际上只能填 \(0\) 了,因为如果填 \(1\) 就超出右边界范围了。
同时,因为同一个集合内的元素一定会相等,因此假如集合内任何一个元素受到了限制,那么整个集合都会因此受到限制。基于这个想法,我们不妨来维护一个数组 \(limit_{i}\),来判断 \(i\) 是否受到了这样的限制。
我们需要寻找出 \(x\) 从右往左第一个不受到限制的集合,并从它的末尾开始统计答案(因为实际上去除最右方的首个受限集合后,剩余的集合就不会再受到限制了,读者不妨自己试举几例)。将限制全部都转移到代表元素上,可以基本总结出下面的流程:
- 假设每个集合的代表元素都在集合的最左边(这很好实现,你只需要在实现并查集的时候将靠右的元素合并到靠左的位置即可)。
- 从右至左依次遍历右边界 \(x\) 的每一位 \(x_{i}\)。
- 假如 \(x_{i}=1\),并且此时代表元素未受到限制,且该集合尚未访问过,则统计答案。统计后将该集合标记为已访问。
- 假如 \(x_{i}=0\),将该集合标记为受限,若该集合已经访问过,则退出循环,表示找到了此位置。
- 假如遇到代表元素受限的元素,说明这个受限的集合已经结束,也退出循环,表示找到了此位置。
现在我们通过此流程找到了第一个不受限制的数位 \(p\),并且 \(p\) 以前的集合有 \(s_{p}\) 个,那么总方案数就为 \(2^{s_{p}}\)。
根据上式可以看出,为了计算这个答案,实际上我们还需维护一个集合数量的前缀数组。
代码步骤2
首先我们考虑到,假如右边界 \(x\ge 2^{k}-1\),那么它一定能够包含全部的 \(2^{a}\) 种情况,这是最简单的。
剩下的步骤即为解法原理3所述。
int ask(int x){
if(++x>=(1ll<<k)){//特殊情况
return (1ll<<s[k]);
}
int ans=0;
memset(limit,-1,sizeof(limit));
//注意清空数组
for(int i=k;i>0;i--){
if(x&(1ll<<i-1)){
if(limit[fa[i]]!=1){
ans+=1ll<<s[i-1];
//统计答案
}
if(fa[i]==i){
limit[i]=1;
//标记访问
}
if(limit[fa[i]]==0){
//步骤5
break;
}
}
else{
//步骤4
if(fa[i]==i){
limit[i]=0;
}
if(limit[fa[i]]==1){
break;
}
}
}
return ans;
}
//主函数的预处理与统计答案部分
for(int i=1;i<=k;i++){
s[i]=s[i-1];
if(find(i)==i){
//预处理集合数量前缀和
s[i]++;
}
}
cout<<ask(r)-ask(l-1);
代码实现
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,k,l,r;
int a[1001],s[61],limit[61];
int fa[61];
int find(int id){
if(fa[id]==id) return id;
fa[id]=find(fa[id]);
return fa[id];
}
void connect(int x,int y){
if(find(x)!=find(y)){
fa[fa[y]]=fa[x];
}
}
bool check(int x,int y){
for(int i=1;i<=n;++i){
if(((a[i]>>x-1)^(a[i]>>y-1))&1) return false;
}
return true;
}
int ask(int x){
if(++x>=(1ll<<k)){
return (1ll<<s[k]);
}
int ans=0;
memset(limit,-1,sizeof(limit));
for(int i=k;i>0;i--){
if(x&(1ll<<i-1)){
if(limit[fa[i]]!=1){
ans+=1ll<<s[i-1];
}
if(fa[i]==i){
limit[i]=1;
}
if(limit[fa[i]]==0){
break;
}
}
else{
if(fa[i]==i){
limit[i]=0;
}
if(limit[fa[i]]==1){
break;
}
}
}
return ans;
}
signed main(){
cin>>n>>k>>l>>r;
for(int i=1;i<=n;++i){
cin>>a[i];
}
for(int i=1;i<=k;++i){
fa[i]=i;
}
for(int i=1;i<=k;++i){
for(int j=i-1;j>0;--j){
if(check(i,j)) connect(i,j);
}
}
for(int i=1;i<=k;i++){
s[i]=s[i-1];
if(find(i)==i){
s[i]++;
}
}
cout<<ask(r)-ask(l-1);
}
[Tkey] 与非的更多相关文章
- 泛型与非泛型集合类的区别及使用例程,包括ArrayList,Hashtable,List<T>,Dictionary<Tkey,Tvalue>,SortedList<Tkey,Tvalue>,Queue<T>,Stack<T>等
泛型与非泛型集合类在C#程序中是非常重要的一个基础概念,这里列一个表来进行对比: 非泛型集合类 泛型集合类 描述 ArrayList List<T> 表示具有动态大小的对象数组 Hasht ...
- MVC5+EF6 简易版CMS(非接口) 第三章:数据存储和业务处理
目录 简易版CMS后台管理系统开发流程 MVC5+EF6 简易版CMS(非接口) 第一章:新建项目 MVC5+EF6 简易版CMS(非接口) 第二章:建数据模型 MVC5+EF6 简易版CMS(非接口 ...
- Linq to BBJECT之非延时标准查询操作符
非延时标准查询操作符是指不具备延时查询特性的标准查询操作符,这些操作符一般用于辅助延时标准查询操作符使用. 1.ToArray操作符 ToArray操作符用于将一个输入序列转换成一个数组. 方法原型: ...
- C#非泛型集合和泛型集合的超级详解
C# 泛型集合之非泛型集合类与泛型集合类的对应: ArrayList对应List HashTable对应Dictionary Queue对应Queue Stack对应Stack SortedList对 ...
- 《LINQ技术详解C#》-5.非延迟操作符
1.转换操作符 1.ToArray 从一个类型为T的输入序列创建一个类型为T的数组. 2.ToList 从一个类型为T的序列创建一个类型为T的列表. 3.ToDictionary 从类型为T的序列创建 ...
- 【bzoj3224】Tyvj 1728 普通平衡树 01Trie姿势+平衡树的四种姿势 :splay,旋转Treap,非旋转Treap,替罪羊树
直接上代码 正所谓 人傻自带大常数 平衡树的几种姿势: AVL Red&Black_Tree 码量爆炸,不常用:SBT 出于各种原因,不常用. 常用: Treap 旋转 基于旋转操作和随机数 ...
- “线程安全的” Dictionary(TKey,TValue)
这是一篇翻译,专门介绍Dictionary线程安全问题,原文网址如下 http://www.grumpydev.com/2010/02/25/thread-safe-dictionarytkeytva ...
- Linq to Object之非延迟标准查询操作符
非延时标准查询操作符是指不具备延时查询特性的标准查询操作符,这些操作符一般用于辅助延时标准查询操作符使用. 1.ToArray操作符 ToArray操作符用于将一个输入序列转换成一个数组. 方法原型: ...
- C#8.0—非空引用类型
非空引用类型--C#8.0 原文地址:https://devblogs.microsoft.com/dotnet/try-out-nullable-reference-types/?utm_sourc ...
- C#中数组、集合(ArrayList)、泛型集合List<T>、字典(dictionary<TKey,TValue>)全面对比
C#中数组.集合(ArrayList).泛型集合List<T>.字典(dictionary<TKey,TValue>)全面对比 为什么把这4个东西放在一起来说,因为c#中的这4 ...
随机推荐
- 第八节 JMeter基础-高级登录【数据库数据驱动】
声明:本文所记录的仅本次操作学习到的知识点,其中商城IP错误,请自行更改. 背景:获取数据库用户表中的数据进行登录接口测试.思路: 引用jar包[测试计划]. 设置数据库的连接信息,取变量名db1-- ...
- 关于Pure中使用RnadomInteger问题的BUG
每一次连线都会造成返回值不一样相当于重新调用了一次,所以返回值需要新建变量存储来使用而不是直接用它拉两次线 下面是错误写法: 正确写法:
- 交叉熵、KL 散度 | 定义与相互关系
1 KL 散度 对于离散概率分布 \(P\) 和 \(Q\) ,KL 散度定义为: \[\text{KL}(P \| Q) = -E_{x\sim P}\log P(x)-\log Q(x) \\ = ...
- 【转载】Win10系统, administrator账户被微软账户强行绑定,怎么破?
首先 声明:这是转载,我只是做一个记录,以下内容可解决问题(本人已尝试并已解决),当然也可以去转载出处查看大佬的原回答: Win10系统, administrator账户被微软账户强行绑定,怎么破? ...
- 初学者使用1Panel面板快速搭建WordPress网站
之前介绍了宝塔面板以及如何搭建wordpress网站,这篇文章我们来学习如何使用1Panel面板搭建wordpress网站. 一.1Panel面板介绍 1. 介绍 1Panel 是一个现代化.开源的基 ...
- Python常见错误及处理
1.ValueError 值错误,传递给函数的变量不符合函数预期类型.如下'python'是非数字无法转换为整数类型,故会报错ValueError 将a改为数字就不会报错了 2.NameError 变 ...
- 1、Springboot2简介
在学习 SpringBoot 之前,建议先具备 SpringMVC(控制层).Spring(业务层)和 Mybatis(持久层)的相关知识 1.1.概述 1.1.1.Spring的缺点 Spring ...
- 2、SpringMVC之入门案例
2.1.环境搭建 2.1.1.右击project创建新module 2.1.2.选择maven 2.1.3.设置module名称和路径 2.1.4.module初始状态 2.1.5.配置打包方式 注意 ...
- 【Linux】Re01
一.三种网络模式 https://www.bilibili.com/video/BV1Sv411r7vd?p=7 1.桥接模式 该虚拟机和宿主机同一网段,和外部网络相通,但是占用网段地址资源,IP分配 ...
- 程序员学习网站推荐:路线向导(roadmap.sh)
网站地址: https://roadmap.sh/ 在外网的技术论坛上看到这个网站,上面给出多种编程语言的学习路线,也就是给出不同编程语言的从易到难的组成内容(语言特性),通过这个网站可以辅助学习编程 ...