Problem: 100246. 将元素分配到两个数组中 II
赛时没做出来,想了个排序,其实排序总假设最坏的情况即倒序,那肯定超时。当时想到线段树了,但是好久没练没搞出来QAQ,我的板子的线段树下标是从1开始的。
(新题题解力扣审的挺严,我随便写的被截了,所以乖乖算了次复杂度嘤嘤嘤~)
思路
我们可以开数组arr1,arr2来统计其中数字出现的数目(下标 i 代表数值,值arr[i]代表出现的次数),然后就可以通过区间和快速得到数组中大于某个数的数目。
因为数据是不断变化的,所以我们得使用树状数组或者线段树。
解题方法
1.离散化
由于数据范围是1~1e9,太大了,不能开1e9的数组。
但是数据量只有1e5,所以我们可以离散化来处理。
离散化后我们要再找这个数的位置,使用二分查找。
复制一个数组tmp = nums,我们对tmp进行排序,即完成的离散化。
(由于要使用线段树,所以下标从1开始。)
2.线段树
然后就是通过线段树获取区间和,我们要的是严格大于当前数的数目,找到这个数的下标,下一个位置到数组末尾这段区间的和就是数目。(可能是最后一个数,下一个位置就越界了,特殊处理一下即可)
参考线段树板子:线段树板子
复杂度
时间复杂度:
O ( n l o g 2 n ) O(nlog_2n) O(nlog2n)
二分 l o g 2 n log_2n log2n,建树 n l o g 2 n nlog_2n nlog2n,区间加 l o g 2 n log_2n log2n,区间修改 l o g 2 n log_2n log2n,遍历数组n,各算法间互补干扰,取最大 n l o g 2 n nlog_2n nlog2n
空间复杂度:
O ( 2 n + n ) O(2^n+n) O(2n+n)
对于线段树数组的计算:
1
∗
2
∗
.
.
.
∗
n
1*2*...*n
1∗2∗...∗n,公比为2的等比数列,
1
∗
2
n
−
1
1
1*\frac{2^{n}-1}{1}
1∗12n−1 即 2^n
再加上原数组大小n
Code
类ST是板子;
getmid是二分找下标;
tmp是离散化后下标对应的值的数组。
#define ll long long
class Solution {
public:
template<class T>
class ST//segment tree
{
struct node
{
T val;
T t;//懒标记//服务后代
node(T v = 0) :val(v), t(0)
{}
};
int n = a.size();
vector<T>a;
vector<node>d;
public:
void build_tree(int i, int l, int r)
{
if (l == r)
{
d[i].val = a[l];
return;
}
int mid = l + (r - l) / 2;
build_tree(i * 2, l, mid);
build_tree(i * 2 + 1, mid + 1, r);
d[i].val = d[i * 2].val + d[i * 2 + 1].val;
}
void spread(int i, int l, int r, int aiml, int aimr)
{
int mid = l + (r - l) / 2;
if (d[i].t != 0 && l != r)
{
d[i * 2].val += d[i].t * (mid - l + 1);
d[i * 2 + 1].val += d[i].t * (r - mid);
d[i * 2].t += d[i].t;//可能上上次也没改
d[i * 2 + 1].t += d[i].t;
d[i].t = 0;
}
}
T getsum(int l, int r)
{
return _getsum(1, 1, n, l, r);
}
T _getsum(int i, int l, int r, int aiml, int aimr)
{
if (aiml <= l && r <= aimr)//查询区间的子集全部加起来
return d[i].val;
//访问
int mid = l + (r - l) / 2;
spread(i, l, r, aiml, aimr);
T ret = 0;
if (aiml <= mid)
ret += _getsum(i * 2, l, mid, aiml, aimr);
if (aimr >= mid + 1)
ret += _getsum(i * 2 + 1, mid + 1, r, aiml, aimr);
return ret;
}
void update(int l, int r, ll val)
{
_update(1, 1, n, l, r, val);//加并挂标记
}
void _update(int i, int l, int r, int aiml, int aimr, ll val)
{
if (aiml <= l && r <= aimr)
{
d[i].val += val * (r - l + 1);
d[i].t += val;
return;
}
int mid = l + (r - l) / 2;
spread(i, l, r, aiml, aimr);
if (aiml <= mid)
_update(i * 2, l, mid, aiml, aimr, val);
if (aimr >= mid + 1)
_update(i * 2 + 1, mid + 1, r, aiml, aimr, val);
//我们只对叶子更新了,(别多想懒标记)
d[i].val = d[i * 2].val + d[i * 2 + 1].val;
}
ST(vector<T>arr)
{
a = arr;
n = a.size() - 1;
d = vector<node>((ll)pow((ll)2, (ll)log2(n) + 1 + 1) + 10);
build_tree(1, 1, n);
}
};
//
vector<int>tmp;
int n;
int getmid(int aim)
{
int l = 0, r = n;
while (l < r)
{
int m = (l + r + 1) / 2;
if (tmp[m] <= aim)l = m;
else r = m - 1;
}
return l;
}
vector<int> resultArray(vector<int>& nums)
{
n = nums.size();
//tmp = nums;
tmp = vector<int>(n + 1);
for (int i = 1; i <= n; i++)
{
tmp[i] = nums[i - 1];
}
sort(tmp.begin() + 1, tmp.end());
vector<int>arr1, arr2, ret;
vector<int>sarr1(n+1), sarr2(n+1);
ST<int>demo1(sarr1), demo2(sarr2);
arr1.push_back(nums[0]);
arr2.push_back(nums[1]);
int t = getmid(nums[0]);
demo1.update(t, t, 1);
t = getmid(nums[1]);
demo2.update(t, t, 1);
for (int i = 2; i < nums.size(); i++)
{
t = getmid(nums[i]);//这个值的下标,我们求右边数目
if (t == n)
{
if (arr1.size() <= arr2.size())
arr1.push_back(nums[i]), demo1.update(t, t, 1);
else
arr2.push_back(nums[i]), demo2.update(t, t, 1);
continue;
}
int r1 = demo1.getsum(t + 1, n), r2 = demo2.getsum(t + 1, n);
if (r1 == r2)
{
if (arr1.size() <= arr2.size())
arr1.push_back(nums[i]), demo1.update(t, t, 1);
else
arr2.push_back(nums[i]), demo2.update(t, t, 1);
}
else if (r1 > r2)
arr1.push_back(nums[i]), demo1.update(t, t, 1);
else
arr2.push_back(nums[i]), demo2.update(t, t, 1);
}
ret = arr1;
for (auto x : arr2)
ret.push_back(x);
return ret;
}
};