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. Linux中的环境变量PS1,打造你的专属终端

    文章目录 介绍 PS1的格式 设置字体样式 举例 小建议 进阶 介绍 好看的终端是怎么做的呢?通过PS1这个环境变量! PS1的格式 PS1='[\u@\h \w]\$ ' 样式: 解释: [是普通字 ...

  2. Windows下USB声卡音量调整

    买了一个绿联的USB声卡, 但是默认的音量太大了,最低音量都响的不行. 查了一下, 发现了一个叫EqualizerAPO的软件可以调整输出设备的音量. https://equalizerapo.com ...

  3. python重拾第十一天-REDIS缓存数据库

    缓存数据库介绍 NoSQL(NoSQL = Not Only SQL ),意即"不仅仅是SQL",泛指非关系型的数据库,随着互联网web2.0网站的兴起,传统的关系数据库在应付we ...

  4. Apache Kylin(三)Kylin上手

    Kylin 上手 根据Kylin 官方给出的测试数据,我们实际操作一下 Kylin. 1. 导入 Hive 数据 首先创建一个project,在界面左上角有个"Add Project&quo ...

  5. 3.8折年终钜惠,RK3568J国产工业评估板

    3.8折年终钜惠,RK3568J国产工业评估板活动火热进行中,错过等一年! -核心板国产化率100%,提供报告-瑞芯微四核ARM Cortex-A55@1.8GHz-4K视频解码.1080P视频编码. ...

  6. NXP i.MX 8M Plus工业开发板硬件说明书( 四核ARM Cortex-A53 + 单核ARM Cortex-M7,主频1.6GHz)

    前  言 本文主要介绍创龙科技TLIMX8MP-EVM评估板硬件接口资源以及设计注意事项等内容. 创龙科技TLIMX8MP-EVM是一款基于NXP i.MX 8M Plus的四核ARM Cortex- ...

  7. jsp+servlet+mysql-----------批量删除

    jsp: <div class="result-wrap"> <form action="${pageContext.request.contextPa ...

  8. Qt中全局变量的定义和使用

    全局变量的定义 现在需要将自定义的STRParameter变量定义为全局变量,就需要新建两个文件,分别命名为"global.h"和"global.cpp" 在g ...

  9. Vue Element-UI 按需引入提示Cannot find module 'babel-preset-es2015'

    1.我的开发环境和操作步骤 1.1.使用VUE-CLI创建  2.x 脚手架 1.2.安装 npm i element-ui(参照官网) 1.3.安装 npm install babel-plugin ...

  10. P9358 题解

    不难发现,最开始有 \(n\) 条链,并且由于每个点最多有一个桥,所以我们的交换操作实际上等价于将相邻的两条链断开,然后将它们后半部分交换.并且每个点在路径中的相对位置不变. 于是考虑维护这些链. 有 ...