[CF498D] Traffic Jams in the Land 题解
看题目似乎没什么思路,但是翻到数据范围,我们发现拥堵程度 \(a_i\) 和修改值的值域是 \([2,6]\),而边权为二时的条件是当前时间 \(t\) 满足 \(t\equiv 0 \pmod{a_i}\),也就是说点 \(i\) 对最终答案的贡献在 \(t\) 属于一定区间时内是相同的,而我们也可以把这个结论扩展到区间上去。
根据以上结论,我们只需要对齐区间内每个值的循环节,就可以保证即使进入区间的 \(t\) 是不同,我们依然可以把 \(t\) 对应到区间循环节的某个部位直接求出区间的贡献,再由数据范围可知,我们最长循环节长度为 60。因为需要支持单点修改,区间查询,并且答案都是从左到右计算,我们考虑使用线段树维护,时间复杂度 \(O(n\log_2n)\),因为要维护循环节的值,会带一个 60 的常数。
Code:
#include <bits/stdc++.h>
#include <bits/extc++.h>
#define ll long long
#define ull unsigned long long
#define m_p make_pair
#define m_t make_tuple
#define N 100010
using namespace std;
using namespace __gnu_pbds;
int val[N], tr[N << 2][60];
void build(int k, int l, int r)
{
if (l == r)
{
for (int i = 0; i < 60; i++)
tr[k][i] = 1 + !(i % val[l]);
return;
}
int mid = l + r >> 1;
build(k << 1, l, mid);
build(k << 1 | 1, mid + 1, r);
for (int i = 0; i < 60; i++)
tr[k][i] = tr[k << 1][i] + tr[k << 1 | 1][(tr[k << 1][i] + i) % 60];
}
void tr_c(int k, int l, int r, int x)
{
if (l > x || r < x)
return;
if (l == r && l == x)
{
for (int i = 0; i < 60; i++)
tr[k][i] = 1 + !(i % val[l]);
return;
}
int mid = l + r >> 1;
if (x <= mid)
tr_c(k << 1, l, mid, x);
else
tr_c(k << 1 | 1, mid + 1, r, x);
for (int i = 0; i < 60; i++)
tr[k][i] = tr[k << 1][i] + tr[k << 1 | 1][(tr[k << 1][i] + i) % 60];
}
int aans;
void tr_a(int k, int l, int r, int x, int y)
{
if (l > y || r < x)
return;
if (l >= x && r <= y)
{
aans += tr[k][aans % 60];
return;
}
int mid = l + r >> 1;
if (x <= mid)
tr_a(k << 1, l, mid, x, y);
if (y > mid)
tr_a(k << 1 | 1, mid + 1, r, x, y);
return;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
int n, q, x, y, ans;
char opt;
cin >> n;
for (int i = 1; i <= n; i++)
cin >> val[i];
cin >> q;
build(1, 1, n);
while (q--)
{
cin >> opt >> x >> y;
if (opt == 'C')
{
val[x] = y;
tr_c(1, 1, n, x);
}
else
{
aans = 0;
tr_a(1, 1, n, x, y - 1);
cout << aans << "\n";
}
}
return 0;
}
关于 tr_a
即询问函数,因为我们知道时间是从左到右计算的,线段树也是从左到右扫的,所以我这里干脆使用了全局变量,这样就可以比较方便的知道某个区间应该是算循环节的哪个部分。
题外话,%你赛考了这题,当时写的是分块,因为脑抽忘记线段树也是从左到右扫的,所以没写线段树,最后十五分钟突然想起来线段树好像也能维护,然后从左到右的查询不知道哪里写锅了,结果没写出来,暴扣 70。