位运算
看n的二进制表示的数字的第k位是几的操作
二进制位数是从0开始的,右移0位表示不动
比如10的二进制表示是1010,右移3位就是1
x中最后一位1的位置
x&(~x+1),~x即x取反,1变0,0变1,然后加1,&x
补码等于源码取反码加一
二进制中1的个数
#include <iostream>
#include <vector>
#include<algorithm>
using namespace std;
const int N = 100010;
int a[N], b[N];
int lowbit(int x)
{
return x & (-x);
}
int main()
{
int n;
cin >> n;
int sum;
while (n--)
{
int e;
cin >> e;
sum = 0;
while (e)
{
e -= lowbit(e);
sum++;
}
cout << sum << ' ';
}
return 0;
}
离散化
排序,去重,快速映射
sort是排序,unique是去重,将重复元素排在最后面,然后返回有效数组的尾指针,最后erase函数删除这些重复的数
二分求出离散化的值
for(auto i: v)遍历容器元素
1. auto
2. auto&
3. const auto&
4. const auto
C++11 新增了一种循环:基于范围(range-based)的 for 循环。这简化了一种常见的循环任务:对数组(或容器类,如 vector 和 array)的每个元素执行相同的操作,如下例所示:
double prices[5] = {4.99, 10.99, 6.87, 6.47, 8.88};
for (double x : prices)
std::cout << x << std::endl;
1
2
3
其中,x xx 最初表示数组 prices 的第一个元素。显示第一个元素后,不断执行循环,而 x xx 依次表示数组的其他元素。因此,该循环可以用来显示数组中的每个值。
要修改数组的元素,需要使用不同的循环变量语法:
for (double &x : prices)
x = x * 0.80; //20% off sale
1
2
符号 & 表明 x xx 是一个引用变量,能让接下来的代码能够修改数组的内容,而第一种语法不能。
还可结合使用基于范围的 for 循环和初始化列表:
for (int x : {3, 5, 2, 8, 6})
cout << x << " ";
cout << '\n';
1
2
3
这种方式可以引申至 C++11 的另一种新特性 auto,使用 auto 遍历容器的方式,有以下几种:
1. auto
auto 即 for(auto x: range) 这样会拷贝一份 range 元素,而不会改变 range 中元素;
2. auto&
当需要修改range中元素,用 for(auto& x: range);
3. const auto&
当只想读取 range 中元素时,使用 const auto&,如:for(const auto&x:range),它不会进行拷贝,也不会修改range;
4. const auto
当需要拷贝元素,但不可修改拷贝出来的值时,使用 for(const auto x:range),避免拷贝开销。
区间和
#include <iostream>
#include <vector>
#include<algorithm>
using namespace std;
const int N = 300010;
typedef pair<int, int> PII;
vector<int> alls;
vector<PII>add, query;
int a[N], s[N];
int find(int x)
{
int l = 0, r = alls.size() - 1;
while (l < r)
{
int mid = l + r >> 1;
if (alls[mid] >= x)
r = mid;
else
l = mid + 1;
}
return r + 1;
}
int main()
{
int n, m;
cin >> n >> m;
for (int i = 0; i < n; i++)
{
int x, c;
cin >> x >> c;
add.push_back({ x,c });
alls.push_back(x);
}
for (int i = 0; i < m; i++)
{
int l, r;
cin >> l >> r;
query.push_back({ l,r });
alls.push_back(l);
alls.push_back(r);
}
//去重
sort(alls.begin(), alls.end());
alls.erase(unique(alls.begin(), alls.end()), alls.end());
for (auto item : add)
{
int x = find(item.first);
a[x] += item.second;
}
//预处理前缀和
for (int i = 1; i <= alls.size(); i++)
s[i] = s[i - 1] + a[i];
//处理询问
for (auto item : query)
{
int l = find(item.first), r = find(item.second);
cout << s[r] - s[l - 1]<<endl;
}
return 0;
}
find()中返回r+1是为了让a数组从1开始,这样前缀和比较方便
前缀和处理的特殊之处,从i = 1开始,结束条件为i<=alls.size()
区间合并
步骤:
#include <iostream>
#include <vector>
#include<algorithm>
using namespace std;
const int N = 300010;
typedef pair<int, int> PII;
vector<PII>segs;
int a[N], s[N];
void merge(vector<PII>& segs)
{
vector<PII> res;
sort(segs.begin(), segs.end());
int st = -2e9, ed = -2e9;
for (auto seg : segs)
if (ed < seg.first)
{
if(st!=-2e9)
res.push_back({ st,ed });
st = seg.first;
ed = seg.second;
}
else
ed = max(ed, seg.second);
if (st != -2e9)
res.push_back({ st,ed });
segs = res;
}
int main()
{
int n;
cin >> n;
for (int i = 0; i < n; i++)
{
int l, r;
cin >> l >> r;
segs.push_back({ l,r });
}
merge(segs);
cout << segs.size();
return 0;
}