#include "bits/stdc++.h"
using namespace std;
#pragma GCC optimize("Ofast", "inline", "-ffast-math")
#pragma GCC target("avx,sse2,sse3,sse4,mmx")
#define int long long
const int K = 4, MAXN = 200000;
const double EPS = 5e-4 / MAXN;
template <class node>
class segment2
{
private:
void build(int l, int r, typename node::info &y)
{
if (l == r)
{
seg[id(l, r)] = node{y, node::empty_tag()};
return;
}
int mid = (l + r) / 2;
build(l, mid, y);
build(mid + 1, r, y);
seg[id(l, r)] = node{node::merge(seg[id(l, mid)].Info, seg[id(mid + 1, r)].Info), node::empty_tag()};
}
void build(int l, int r, vector<typename node::info> &y)
{
if (l == r)
{
seg[id(l, r)] = node{y[l], node::empty_tag()};
return;
}
int mid = (l + r) / 2;
build(l, mid, y);
build(mid + 1, r, y);
seg[id(l, r)] = node{node::merge(seg[id(l, mid)].Info, seg[id(mid + 1, r)].Info), node::empty_tag()};
}
void push_down(int l, int r)
{
int mid = (l + r) / 2, ls = id(l, mid), rs = id(mid + 1, r), pt = id(l, r);
seg[ls] = {node::merge(seg[ls].Info, seg[pt].Tag), node::merge(seg[ls].Tag, seg[pt].Tag)};
seg[rs] = {node::merge(seg[rs].Info, seg[pt].Tag), node::merge(seg[rs].Tag, seg[pt].Tag)};
seg[pt].Tag = node::empty_tag();
}
void change(int L, int R, int l, int r, typename node::tag &y)
{
if (L <= l && r <= R)
{
seg[id(l, r)] = {node::merge(seg[id(l, r)].Info, y), node::merge(seg[id(l, r)].Tag, y)};
return;
}
int mid = (l + r) / 2;
push_down(l, r);
if (L <= mid)
change(L, R, l, mid, y);
if (R > mid)
change(L, R, mid + 1, r, y);
seg[id(l, r)].Info = node::merge(seg[id(l, mid)].Info, seg[id(mid + 1, r)].Info);
}
typename node::info query(int L, int R, int l, int r)
{
if (L <= l && r <= R)
return seg[id(l, r)].Info;
push_down(l, r);
int mid = (l + r) / 2;
if (L > mid)
return query(L, R, mid + 1, r);
else if (R > mid)
return node::merge(query(L, R, l, mid), query(L, R, mid + 1, r));
else
return query(L, R, l, mid);
}
void change(int x, int l, int r, typename node::info &y)
{
if (l == r)
{
seg[id(x, x)] = {y, node::empty_tag()};
return;
}
int mid = (l + r) / 2;
push_down(l, r);
if (x <= mid)
change(x, l, mid, y);
if (x > mid)
change(x, mid + 1, r, y);
seg[id(l, r)].Info = node::merge(seg[id(l, mid)].Info, seg[id(mid + 1, r)].Info);
}
public:
int id(int l, int r) { return (l + r) | (l != r); }
vector<node> seg;
int siz;
void init(int n, typename node::info y)
{
siz = n;
seg.resize(2 * n + 4);
build(1, siz, y);
}
void init(int n, vector<typename node::info> y)
{
siz = n;
seg.resize(2 * n + 4);
build(1, siz, y);
}
void change(int l, int r, typename node::tag y) { change(l, r, 1, siz, y); }
void change(int x, typename node::info y) { change(x, 1, siz, y); }
typename node::info query(int l, int r) { return query(l, r, 1, siz); }
segment2()
{
}
segment2(int n, typename node::info y)
{
init(n, y);
}
segment2(vector<typename node::info> y)
{
// drop first
init(y.size() - 1, y);
}
};
int mapping[K + 1][K + 1], a[MAXN + 1];
double bin[(K + 1) * (K + 2) / 2], pw[K + 1];
struct node
{
struct info
{
double f[(K + 1) * (K + 2) / 2];
double res()
{
double sum = 0;
for (int i = 0; i <= K; ++i)
sum += f[mapping[i][i]];
return sum;
}
} Info;
struct tag
{
double add;
} Tag;
static tag empty_tag()
{
return {0};
}
static info merge(info a, info b)
{
for (int i = 0; i < (K + 1) * (K + 2) / 2; ++i)
a.f[i] += b.f[i];
return a;
}
static tag merge(tag a, tag b)
{
return {a.add + b.add};
}
static info merge(info a, tag b)
{
if (b.add == 0)
return a;
for (int i = 1; i <= K; ++i)
pw[i] = pw[i - 1] * b.add;
for (int i = 0; i <= K; ++i)
for (int j = i; j >= 0; --j)
for (int k = 0; k < j; ++k)
a.f[mapping[i][j]] += bin[mapping[j][k]] * pw[j - k] * a.f[mapping[i][k]];
return a;
}
};
template <class node>
class segment1
{
private:
void change(int l, int r, int x, node &y)
{
if (l == r)
{
seg[id(l, r)] = y;
return;
}
int mid = (l + r) / 2;
if (x <= mid)
change(l, mid, x, y);
else
change(mid + 1, r, x, y);
seg[id(l, r)] = node::merge(seg[id(l, mid)], seg[id(mid + 1, r)]);
}
node query(int L, int R, int l, int r)
{
if (l <= L && R <= r)
return seg[id(L, R)];
int mid = (L + R) / 2;
if (l <= mid && r > mid)
return node::merge(query(L, mid, l, r), query(mid + 1, R, l, r));
if (l <= mid)
return query(L, mid, l, r);
return query(mid + 1, R, l, r);
}
void build(int l, int r, node &y)
{
if (l == r)
{
seg[id(l, r)] = y;
return;
}
int mid = (l + r) / 2;
build(l, mid, y);
build(mid + 1, r, y);
seg[id(l, r)] = node::merge(seg[id(l, mid)], seg[id(mid + 1, r)]);
}
public:
int id(int l, int r) { return (l + r) | (l != r); }
vector<node> seg;
int siz;
void init(int n, node y)
{
siz = n;
seg.resize(2 * n + 4);
build(1, siz, y);
}
void change(int x, node y) { change(1, siz, x, y); }
node query(int l, int r) { return query(1, siz, l, r); }
segment1(int n, node y)
{
init(n, y);
}
segment1()
{
}
};
struct nodesum
{
long long value;
static inline nodesum merge(nodesum a, nodesum b)
{
return {a.value + b.value};
}
};
int seg_bin(int x, segment1<nodesum> &seg)
{ // first >= x
int l = 1, r = seg.siz, mid, s = 0;
while (l != r)
{
mid = (l + r) / 2;
if (s + seg.seg[seg.id(l, mid)].value < x)
{
s += seg.seg[seg.id(l, mid)].value;
l = mid + 1;
}
else
r = mid;
}
if (seg.seg[seg.id(1, seg.siz)].value < x)
return seg.siz + 1;
return l;
}
double taylor_series(int x, int x0)
{
double result = 0.0;
double term = 1.0 / x0;
for (int i = 0; i <= K; ++i)
{
result += term;
term *= -(double)(x - x0) / (x0);
}
return result;
}
pair<int, int> nxt(int x)
{
int l = x, r = 1.0 / EPS, m, s1 = x, s2 = x;
while (l <= r)
{
m = (l + r) / 2;
if (fabs(taylor_series(x, m) - 1.0 / x) > EPS)
r = m - 1;
else
{
l = m + 1;
s1 = m;
}
}
l = s1, r = 1.0 / EPS;
while (l <= r)
{
m = (l + r) / 2;
if (fabs(taylor_series(m, s1) - 1.0 / m) > EPS)
r = m - 1;
else
{
l = m + 1;
s2 = m;
}
}
return {s1, s2};
}
int n, q, o[MAXN + 1], l[MAXN + 1], r[MAXN + 1], x[MAXN + 1];
vector<pair<int, int>> scan[MAXN + 1];
vector<array<int, 3>> scan1[MAXN + 1];
signed main()
{
ios_base::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
for (int i = 0, cnt = 0; i <= K; ++i)
for (int j = 0; j <= i; ++j)
mapping[i][j] = cnt++;
for (int i = 0, cnt = 0; i <= K; ++i)
for (int j = 0; j <= i; ++j)
{
mapping[i][j] = cnt++;
if (j == 0 || j == i)
bin[mapping[i][j]] = 1.0;
else
bin[mapping[i][j]] = bin[mapping[i - 1][j - 1]] + bin[mapping[i - 1][j]];
}
pw[0] = 1;
vector<int> poly_segment;
vector<array<double, K + 1>> poly;
for (int i = 1; i <= 1.0 / EPS;)
{
poly_segment.push_back(i);
array<double, K + 1> val = {};
for (int j = 1; j <= K; ++j)
pw[j] = pw[j - 1] * nxt(i).first;
for (int j = 0; j <= K; ++j)
for (int k = 0; k <= j; ++k)
val[k] += bin[mapping[j][k]] / pw[k] / nxt(i).first * (k % 2 == 0 ? 1 : -1);
poly.push_back(val);
i = nxt(i).second + 1;
}
poly_segment.push_back(1 / EPS);
poly.push_back({{0}});
int t;
cin >> t;
while (t--)
{
vector<node::info> temp = {{}};
cin >> n >> q;
for (int i = 1; i <= n; i++)
{
o[i] = l[i] = x[i] = r[i] = a[i] = 0;
scan[i].clear();
scan1[i].clear();
}
for (int i = 1; i <= n; ++i)
{
cin >> a[i];
for (int j = 1; j <= K; ++j)
pw[j] = pw[j - 1] * a[i];
node::info q = {{}};
for (int j = 0, idx = lower_bound(poly_segment.begin(), poly_segment.end(), a[i] + 1) - poly_segment.begin() - 1; j <= K; ++j)
for (int k = 0; k <= j; ++k)
q.f[mapping[j][k]] = poly[idx][j] * pw[k];
temp.push_back(q);
}
segment2<node> main_tree(temp);
segment1<nodesum> scanline(q, {0});
for (int i = 1; i <= n; ++i)
scan[i].push_back({1, a[i]});
for (int i = 1; i <= q; ++i)
{
cin >> o[i] >> l[i] >> r[i];
if (o[i] == 1)
{
cin >> x[i];
scan[l[i]].push_back({i + 1, x[i]});
if (r[i] != n)
scan[r[i] + 1].push_back({i + 1, 0});
}
}
for (int i = 1; i <= n; ++i)
{
for (pair<int, int> j : scan[i])
scanline.change(j.first, {j.second});
int val = -1, lastval = -1;
array<int, 4> lastoperation = {-1, -1, -1, -1};
for (unsigned int j = 0; j < poly_segment.size(); ++j)
{
val = seg_bin(poly_segment[j], scanline);
if (val != 1 && val != q + 2 && lastoperation[3] != 0 && val != lastval)
{
scan1[lastoperation[3]].push_back(array<int, 3>{lastoperation[0], lastoperation[1], lastoperation[2]});
}
lastval = val;
lastoperation = {i, (int)j, scanline.query(1, val).value, val - 1};
}
if (scanline.seg[scanline.id(1, q)].value >= poly_segment.end()[-1])
{
scan1[lastoperation[3]].push_back(array<int, 3>{lastoperation[0], lastoperation[1], lastoperation[2]});
}
}
for (int i = 1; i <= q; ++i)
{
if (o[i] == 1)
{
main_tree.change(l[i], r[i], {x[i]});
}
for (array<int, 3> j : scan1[i])
{
node::info q = {{}};
for (int k = 0; k <= K; ++k)
q.f[mapping[k][0]] = poly[j[1]][k];
q = node::merge(q, node::tag{double(j[2])});
main_tree.change(j[0], q);
}
if (o[i] == 2)
cout << main_tree.query(l[i], r[i]).res() << endl;
}
}
}
This blog would be readable if you actually describe what you approximated $$$\frac{1}{x}$$$ with, and what brackets are they split into, and not expect everyone to read it from the code -- if only you desrcibe them in as much detail as the not-fast-enough solution.
As far as I am aware, if you directly approximate $$$\frac{1}{a+x}$$$ as taylor series, error would be extreme as soon as $$$x$$$ is on similar size of $$$a$$$. There are ways to mitigate this but that is me solving the problem and not reading your solution.
Another problem that uses Taylor expansion: https://oj.vnoi.info/problem/vmsincos, but the error of approximation on this problem is not that big (I used 7-term Taylor expansion and it passed).