Logtrick
logtrick的用法与实战
logtrick是我从灵神视频中学习到的,此文章介绍logtrick用法与实践,以及灵神视频中未提到的,我本人总结出来的小技巧
用法
logtrick通常用于求 子数组(gcd,lcm,&,|)后的max或者min或者计数问题
子数组问题
logtrick主要是解决子数组问题,所以在此我们先引出子数组问题
什么是子数组问题?
就是求 f(l,r)的所有操作的(最优值 或者 计数值) 1<=l<=r<=n
类似这个公式:

但不一定是求和,应是题目要求的求(最优值 或者 计数值)等
**这个f即函数 是求区间[l,r]完成题目特定操作后 得到的特定值 **
比如说:f(l,r),f函数是求min的 那么就是求[l,r]区间中最小的一个值
如何高效计算子数组问题
计算子数组,有一种可行思路是把注意力放在右端点,通过不断移动右端点,在移动的时候利用前面的统计结果算出移动右端点后的结果,从而得出所有子区间的答案。
这种方法经典套路有 前缀和,logtrick
今天我们重点是logtrick
logtrick是基于O(n^2)的计算子数组问题 利用(|,&,lcm,gcd)等性质 优化的一种套路亦或者是算法
先介绍o(n^2)的算法
for(int i = 0; i < n;i++){
for(int j = i - 1;j >= 0;j--){
a[j] ?= a[i]; //此问号可以为gcd lcm | &
}
}
这个算法挺清晰的我们研究一下过程,然后考虑优化
比如说 求a的子数组按位&等于k的子数组的总和
给出数据与对应二进制:
7 3 4 1
111 011 100 001
操作流程:

可以发现这样枚举右端点更新左端点可以保证求出所有的子数组的运算结果
优化:
这是最最最重点的了我们发现第三次到第四次过程中是没有变化的,为什么呢,因为&越多数&的的总结局是越来越小
而&不变就是说明了我这个数已经不足以让当前位置变小,又因为当前位置的前面都是与当前位置&过的,所以不能让当前位置变小也自然不会让前面位置再变小,这点很难理解用灵神的话来讲就是看做是集和的关系,我后面枚举的这个数设为x & 当前这个数设为y然后 y不变小, 意味这 y 一定是x的子集 而y前面的数都&过y 说明都是与y的交集了,与x交就相当于没有交; 很难理解借助图形理解
图形
我们设当前枚举的是x 然后 x&y=y , 然后y前面的数为z z&y设为 t
前面的数组的记录的数t是这样的

y是x的子集所以加入x长这样

虚线部分是x,可以看到加入x是不会对y和y前面的t产生影响的 因为 x与y与z的交集还是t
所以当我们遇到 a[j] & a[i] == a[j] 时 直接break ,不必管前面的数
就因为这个小优化我们的时间复杂度可以从
O(n^2) 变为 O(nlogU)
u为数组中最大值
因为我们一个数不断缩小就加强了成为x的子集的可能性,就更可能的触及 a[j] & a[i] == a[j] 这个条件,而最坏也是log级别的缩小,此为logtrick精髓所在
除了& 还有| gcd lcm 殊途同归读者自己思考
模版
for(int i = 1; i <= n;i++){
int x = nums[i];
//这里可以进行一些操作
for(int j = i - 1;j >= 1;j--){
if((nums[j] 操作 nums[i]) = ?){
break;
}
nums[j] 操作= nums[i];
}
然后进行二分或者一些操作
}
小技巧
logtrick 配合 map使用有出其不意的效果,甚至说更加无脑与方便,下面题目有讲
实战
简单题
源于灵神总结的题单
力扣3097
3097. 或值至少为 K 的最短子数组 II - 力扣(LeetCode)
思路: 因为如果 nums[j] | nums[i] == nums[i] 就代表前面都不会再变大,所以直接结束,很经典的板子题
class Solution {
public:
int minimumSubarrayLength(vector<int>& nums, int k) {
int mi = 0x3f3f3f3f;
int n = nums.size();
for(int i = 0; i < n;i++){
if(nums[i] >= k){
return 1;
}
for(int j= i - 1; j >= 0 && (nums[j] | nums[i]) != nums[j];j--){
nums[j] |= nums[i];
if(nums[j]>=k){
mi = min(mi,i-j+1);
}
}
}
return mi==0x3f3f3f3f ? -1 : mi;
}
};
力扣2411
2411. 按位或最大的最小子数组长度 - 力扣(LeetCode)
思路:和上题类似
class Solution {
public:
vector<int> smallestSubarrays(vector<int>& nums) {
int n = nums.size();
vector<int> ans(n,1);
for(int i = 0; i < n;i++){
int x= nums[i];
for(int j = i - 1 ;j >= 0 && (nums[j] | nums[i]) != nums[j];j--) {
nums[j]|=nums[i],ans[j] = i - j + 1;
}
}
return ans;
}
};
力扣3209
3209. 子数组按位与值为 K 的数目 - 力扣(LeetCode)
思路: 该题更快做法应该是二分,但是这里我们用无脑的map计数直接无视二分,让问题难度直线下降
class Solution {
public:
long long countSubarrays(vector<int>& nums, int k) {
long long ans = 0;
int n = nums.size();
map<int,int> h;
for(int i = 0;i<n;i++){
int x = nums[i];
h[x]++;
for(int j = i - 1; j >= 0 && (nums[j] & nums[i]) != nums[j];j--){
h[nums[j]]--;
nums[j]&=nums[i];
h[nums[j]]++;
}
ans+=h[k];
}
return ans;
}
};
力扣1521
1521. 找到最接近目标值的函数值 - 力扣(LeetCode)
思路: 简单板子运用
class Solution {
public:
int closestToTarget(vector<int>& nums, int target) {
int ans = 0x3f3f3f3f;
for(int i = 0;i < nums.size();i++){
ans = min(ans,abs(nums[i] - target));
for(int j = i - 1;j >= 0;j--){
if((nums[j]&nums[i]) == nums[j]) break;
nums[j]&=nums[i];
ans = min(ans,abs(nums[j] - target));
}
}
return ans;
}
};
力扣898
思路:计数问题带个map 会好解决很多
class Solution {
public:
int subarrayBitwiseORs(vector<int>& a) {
int n = a.size();
map<int,int> h;
for(int i = 0;i<n;i++){
h[a[i]]++;
for(int j = i - 1; j >= 0;j--){
if((a[j]|a[i])==a[j]) break;
h[a[j]|a[i]]++;
a[j]|=a[i];
}
}
return h.size();
}
};
中等题
CF475D
思路: 运用map然后累加到res中
const int N = 200005;
int n,m;
map<int,int> res;
int a[N];
void solve(){
cin >> n;
for(int i = 1; i <= n;i++){
cin >> a[i];
}
map<int,int> h;
for(int i = 1; i <= n;i++){
h[a[i]]++;
for(int j = i - 1; j >=1;j--){
if(__gcd(a[i],a[j]) == a[j]){
break;
}else{
h[a[j]]--;
if(h[a[j]]==0) h.erase(a[j]);
a[j] = __gcd(a[i],a[j]);
h[a[j]]++;
}
}
for(auto p:h) res[p.first]+=p.second;
}
int q;
cin >> q;
while(q--){
int t;
cin >> t;
cout << res[t] <<endl;
}
}
困难题
CF1632D
此题是我做到目前为止 logtrick中最难的题,但是其实也不是很难啦,就是相对别的题目比较难,就是要灵活掌握变形
思路:思维+logtrick,首先我们要清楚要贪心修改数字必定是把数字修改成1,因为这样才不会影响后面的数,然后就是如果修改了前面的子数组的一个元素,前面的子数组就全部没用了,直接map.claer(),然后做logtrick时候j那一维度为不用遍历到前面了
const int N = 200005;
int n,m;
map<int,int> res;
int a[N];
void solve(){
cin >> n;
for(int i = 1; i <= n;i++){
cin >> a[i];
}
map<int,set<int> > h;
int cnt = 0;
int lt = 1;
for(int i = 1; i <= n;i++){
h[a[i]].insert(i);
for(int j = i - 1;j>=lt;j--){
if(__gcd(a[j],a[i])==a[j]){
break;
}
h[a[j]].erase(j);
if(h[a[j]].size()==0) h.erase(a[j]);
h[__gcd(a[j],a[i])].insert(j);
a[j]=__gcd(a[j],a[i]);
}
//i-j+1 == k
bool f = false;
for(auto &[k,se]:h){
if(se.count(i - k + 1)){
cnt++;
f = true;
break;
}
}
if(f){
map<int,set<int> > h2;
h=h2;
h[1].insert(i);
lt=i+1;
}
cout << cnt << " ";
}
}
最后感谢@灵茶山艾府
基础起源于灵神的教学,题单也是灵神那里找的
Logtrick的更多相关文章
- Flink + 强化学习 搭建实时推荐系统
如今的推荐系统,对于实时性的要求越来越高,实时推荐的流程大致可以概括为这样: 推荐系统对于用户的请求产生推荐,用户对推荐结果作出反馈 (购买/点击/离开等等),推荐系统再根据用户反馈作出新的推荐.这个 ...
随机推荐
- .NET Core MVC基础之页面传值方式📃
.NET Core MVC基础之页面传值方式 前言 最近工作太忙了,浅浅更新一下.NET基础知识.大部分面试官都会问.NET页面传值的几种方式,那么接下来就来细讲与实现一下吧! 页面传值分成两类 第一 ...
- 高通Android平台 电池 相关配置
背景 在新基线上移植有关的代码时,在log中发现有关的东西,请教了有关的同事以后,解决了这个问题. [ 12.775863] pmi632_charger: smblib_eval_chg_termi ...
- post基础错误注入
Burpsuite抓取HTTP请求 Burpsuite是一款Web安全测试的利器,集成了几乎Web安全测试中所有需要用到的功能. 运行前提: 需要安装Java https://www.java.com ...
- mirai Bot初始化配置
RT 其实本来我的bot已经因为自己手贱登陆qq nt直接报废了,但是论坛里有佬提供了新的协议库,那这不赶紧复活bot都对不起这个新的协议库. 本文写于2024年7月4日19:20:21,可能随着时间 ...
- ABP框架开发实例教程-获取前端样式和脚本
1.运行生成的ABP框架,设置WEB.MVC项目为启动项目,点击开始运行,界面如下,样式和脚本文件不存在,所以导致界面无法正常显示,这个我研究ABP遇到的第二个坑,第一个坑是生成数据库. 2.为什么没 ...
- ASP.NET Core修改CentOS的IP地址
最近做的一个产品中有个需求,就是客户使用的时候可以通过Web网页修改服务器的IP地址(客户是普通使用者,没有Linux使用经验,我们的产品作为一台服务器部署到客户机房,客户通过HTTP方式访问使用). ...
- Unable to start web server; nested exception is org.springframework.context.ApplicationContextException
项目报错:Unable to start web server; nested exception is org.springframework.context.ApplicationContextE ...
- KubeCube 用户管理与身份认证
前言 KubeCube (https://kubecube.io) 是由网易数帆近期开源的一个轻量化的企业级容器平台,为企业提供 kubernetes 资源可视化管理以及统一的多集群多租户管理功能.K ...
- 在Visual Studio Code中,鼠标双击PHP变量的时候,如何选择包括$在内的整个变量名
依次点击:文件->首选项->设置 并在"editor.wordSeparators"设置中为您的语言指定删除"$"符号:
- 解决阿里云redis监听6379,配置规则也将6379端口开放,但是外网仍无法连接6379的问题
首先确保阿里云配置规则和服务器防火墙已开发6379端口 阿里云linux安装完成redis,并且已经运行,检测6379端口,显示redis-server正在监听,如图 修改redis.conf配置 将 ...