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

898. 子数组按位或操作 - 力扣(LeetCode)

思路:计数问题带个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

Problem - 475D - Codeforces

思路: 运用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中最难的题,但是其实也不是很难啦,就是相对别的题目比较难,就是要灵活掌握变形

Problem - 1632D - Codeforces

思路:思维+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的更多相关文章

  1. Flink + 强化学习 搭建实时推荐系统

    如今的推荐系统,对于实时性的要求越来越高,实时推荐的流程大致可以概括为这样: 推荐系统对于用户的请求产生推荐,用户对推荐结果作出反馈 (购买/点击/离开等等),推荐系统再根据用户反馈作出新的推荐.这个 ...

随机推荐

  1. JS的JSON.parse问题

    这个问题,已经有非常多人说过,而且由来已久. 大家都提供了不少的解决方法,但是都不够彻底. 一)现在是什么情况 1.使用SpringMvc+ModelAndView+jsp传递值 由于业务需要,通过m ...

  2. 一款.NET开源的i茅台自动预约小助手

    前言 今天大姚给大家分享一款.NET开源.基于WPF实现的i茅台APP接口自动化每日自动预约(抢茅台)小助手:HyggeImaotai. 项目介绍 该项目通过接口自动化模拟i茅台APP实现每日自动预约 ...

  3. 基于LoRA的RLHF

    参考Github 开源模型LLM-Tuning 一.简介 (1)RLHF (基于人类反馈的强化学习) 分为三步: SFT (Supervised Fine-Tuning): 有监督的微调,使用正常的 ...

  4. ubuntu 安装 arm-none-eabi-gcc 的几种方式

    背景 这篇文章主要是为了解决 在 Linux 中 编译 能够在 裸机上 跑的 程序 目前许多嵌入式软件软件开发在Linux平台下进行,编译效率高很多,如今天所述的gcc-arm-none-eabi常适 ...

  5. P3355 骑士共存问题题解

    题目链接:P3355 骑士共存问题 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 题解: 棋盘问题考虑黑白染色成为二分图后做. 观察马的性质,可知一个点只能到一个异色点,所以,构造 ...

  6. FPGA案例开发手册——基于全志T3+Logos FPGA核心板

    前 言 本文档主要提供评估板FPGA端案例测试方法,适用的开发环境为Windows 7 64bit和Windows 10 64bit. 本文案例基于创龙科技的全志T3+Logos FPGA核心板,它是 ...

  7. 多个子节点收集日志-主节点上传到HDFS

    Master: ---------------------- #MasterAgentMasterAgent.channels = c1MasterAgent.sources = s1MasterAg ...

  8. yb课堂 注册-登陆-个人模块 《三十九》

    Cube-UI的form表单 https://didi.github.io/cube-ui/#/zh-CN/docs/form 注册 Register.vue <template> < ...

  9. Eureka 客户端依赖管理模块

    <dependencies> <!--Eureka客户端依赖--> <dependency> <groupId>org.springframework. ...

  10. move语义和完美转发

    move语义 值类别(value category)如下: lvalue:左值,在内存中有地址,可被程序员访问,可以放在赋值运算符左侧,也可以放在赋值运算符右侧,常见的左值有普通变量.字符串字面值&q ...