P10083 [GDKOI2024 提高组] 不休陀螺 题解
显然我们要解决的主要问题就是能“陀螺无限”的充要条件。我们坚信推出来后是可以维护的。
我们设我们要打出的这些牌的 分别是 和 。
容易观察到一个必要条件是 ,否则每打完一次能量总是降低,最终一定无法无限打下去。
但是这个条件不充分。反方向考虑什么时候打不完。如果存在一种方法使得无法无限打,那么一定是在第一局。因为在后面的局数中,能量总是不小于第一局的初始值,故不优。
那么如果存在 和 的一种排列,使得存在 ,,就无法无限打下去。此时无法打出第 张牌。
这时考虑反方,即不想让你打完的会怎么想。肯定是把 的全都放在前面,然后放某个 。记 。
考虑任意一个 ,如果 ,那么这张牌没有被选过,此时需要判断 与 的关系。移项得 与 的关系。
如果 ,那么此时需要判断 与 的关系。移项得 与 的关系。
发现 时是 , 时是 。所以是 。
能无限打的充要条件是 的和大于等于 ,且对于任意 ,有 成立。
第一个条件不太好处理,先考虑第二个,一定是 成立。
如果枚举左端点 ,二分 。 随着 增大而单调不降, 显然也是单调不降。所以右边式子有单调性,可以二分右端点求出每个 对应的最大的 。可以用 ST 表和前缀和处理这一部分。
但是还有一点在于上文提到的难以维护的限制,即 的和大于等于 。如果使用前缀和 。那么上面的限制等价于 。注意到枚举左端点时, 和 是定值,进行上文的二分右端点后问题转化成区间 中的 大于等于一个定值的问题。这是一个二维偏序,显然可以离线树状数组或者在线主席树维护。
于是问题在 的时空复杂度内得以解决。
主席树代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5;
int a[N], b[N];
long long sumc[N];
int n;
long long e;
int f[N][22], LG2[N];
void Init()
{
for (int i = 2; i < N; i++) LG2[i] = LG2[i >> 1] + 1;
for (int i = 1; i <= n; i++) f[i][0] = min(a[i], b[i]);
for (int j = 1; j <= LG2[n]; j++)
{
for (int i = 1; i + (1 << j) - 1 <= n; i++)
{
f[i][j] = max(f[i][j - 1], f[i + (1 << (j - 1))][j - 1]);
}
}
}
int query(int l, int r)
{
int p = LG2[r - l + 1];
return max(f[l][p], f[r - (1 << p) + 1][p]);
}
int rt[N];
class Chariman_Tree
{
public:
int idx;
struct Node
{
int lson, rson, sum;
}tr[N * 26];
void pushup(int u)
{
tr[u].sum = tr[tr[u].lson].sum + tr[tr[u].rson].sum;
}
int ins(int p, int u, int v, int l, int r)
{
u = ++idx;
tr[u] = tr[p];
if (l == r)
{
tr[u].sum++;
return u;
}
int mid = (l + r) >> 1;
if (v <= mid) tr[u].lson = ins(tr[p].lson, tr[u].lson, v, l, mid);
else tr[u].rson = ins(tr[p].rson, tr[u].rson, v, mid + 1, r);
pushup(u);
return u;
}
int query(int p, int q, int l, int r, int nl, int nr)
{
if (!p && !q) return 0;
if (l >= nl && r <= nr) return tr[q].sum - tr[p].sum;
int mid = (l + r) >> 1;
int ans = 0;
if (nl <= mid) ans = query(tr[p].lson, tr[q].lson, l, mid, nl, nr);
if (nr > mid) ans += query(tr[p].rson, tr[q].rson, mid + 1, r, nl, nr);
return ans;
}
}sgt;
long long sumd[N];
int main()
{
ios::sync_with_stdio(0), cin.tie(0);
cin >> n >> e;
long long ans = 0LL;
for (int i = 1; i <= n; i++) cin >> a[i];
vector<long long> vv;
for (int i = 1; i <= n; i++)
{
cin >> b[i];
sumc[i] = sumc[i - 1] + max(0LL, (long long)a[i] - b[i]);
sumd[i] = sumd[i - 1] + b[i] - a[i];
vv.emplace_back(sumd[i]);
}
vv.emplace_back(0);
sort(vv.begin(), vv.end());
vv.erase(unique(vv.begin(), vv.end()), vv.end());
for (int i = 0; i <= n; i++) sumd[i] = lower_bound(vv.begin(), vv.end(), sumd[i]) - vv.begin() + 1;
for (int i = 0; i <= n; i++)
{
rt[i] = sgt.ins((i ? rt[i - 1] : 0), rt[i], sumd[i], 1, n + 1);
}
Init();
for (int i = 1; i <= n; i++)
{
int l = i, r = n, place = -1;
while (l <= r)
{
int mid = (l + r) >> 1;
long long g = e - (sumc[mid] - sumc[i - 1]);
if (query(i, mid) <= g) l = mid + 1, place = mid;
else r = mid - 1;
}
if (place != -1) ans += sgt.query(rt[i - 1], rt[place], 1, n + 1, sumd[i - 1], n + 1);
//cout << i << " " << place << "\n";
}
//cout << (double)(&st - &ed) / 1024.0 / 1024.0 << "\n";
cout << ans << "\n";
return 0;
}

浙公网安备 33010602011771号