P8379 [PFOI Round1] Two Operations 题解
本蒟蒻不会写 multiset,于是写了一发优先队列(逃)。 如有谬误,大家敬请指正。
Sol
首先考虑操作 Query。
设
合并方式
设
可以发现,Change 后
我们再来研究一下 Change 操作对序列
设共有四个组
-
对于
G_{1} 和G_{4} ,修改操作并不影响它们的最大值,因此w_{1} 和w_{4} 的值不改变。即:w_{1}' = w_{1} = v_{1} + \frac{f_{2} + f_{3} + f_{4}}{S_{1} + S_{2}+ S_{3} + S_{4}},w_{4}' = w_{4} = v_{4} + \frac{f_{1} + f_{2} + f_{3}}{S_{1} + S_{2}+ S_{3} + S_{4}} -
对于
G_{3} ,进行分类讨论:-
如果
v_{2} \leq v_{3} ,w_{3}' = v_{3} + \frac{f_{1} + f_{4}}{S_{1} + S_{2}+ S_{3} + S_{4}} ,有w_{3}' < w_{3} 。 -
如果
v_{2} > v_{3} ,w_{3}' = v_{2} + \frac{f_{1} + f_{4}}{S_{1} + S_{2}+ S_{3} + S_{4}} ,有w_{3}' < w_{2} 。
-
综上所述,可得引理 2:当
这样就可以用优先队列维护了。
不难发现,对于每一次 Change,都会对应着一个组被删除,一个
而对于存留下来的另一个 Change 前的
开一个 Change 的问题了。
具体实现请看代码 (QwQ)
代码
ll n, m, k, sum = 0;
ll a[MAXN], b[MAXN];
ll f[MAXN], v[MAXN], S[MAXN], w[MAXN], fa[MAXN];
bool flag[MAXN];
struct item
{
ll pos, val; // pos 表示 G_{i} 中最大值的原下标
bool operator<(const item& x)const
{
return x.val < val;
}
};
priority_queue<item>q;
void init() // 读入并初始化
{
n = read(), m = read(), k = read();
for (int i = 1; i <= n; i++) a[i] = read();
for (int i = 1; i <= n; i++) b[i] = read();
for (int i = 1; i <= n; i++)
S[a[i]]++, f[a[i]] += b[i], v[a[i]] = max(v[a[i]], b[i]), sum += b[i];
for (int i = 1; i <= n; i++)
w[i] = v[i] * n + sum - f[i]; // 计算 w(分子部分)
for (int i = 1; i <= k; i++)
q.push({i, w[i]}); // 将所有 w push 进队列里
for (int i = 1; i <= n; i++) fa[i] = i; // 初始化所有组中的最大值下标
}
ll pow(ll x, ll temp)
{
ll res = 1;
for (; temp; temp >>= 1, x = x * x % p)
if (temp & 1) res = res * x % p;
return res;
}
int main()
{
init();
while (m--)
{
string op;
cin >> op;
if (op == "Change")
{
int x, y;
x = read(), y = read();
f[y] += f[x]; //update
S[y] += S[x];
if (max(v[x], v[y]) == v[x])
flag[fa[y]] = true, fa[y] = fa[x]; // 更新 G_{y} 中的最大值下标
else
flag[fa[x]] = true; // G_{x} 被删除,不用更新 fa_{x}
v[y] = max(v[x], v[y]); // 更新最大值
ll z = v[y] * n + sum - f[y]; // 计算 w'(分子部分)
q.push({fa[y], z}); // 将 G_{y} 中最大值的原下标和 w' push 进队列
}
else if (op == "Query")
{
while (flag[q.top().pos] == true) // 如果队头元素中包含的最大值已被淘汰
{
q.pop();
}
ll ans = ((q.top().val % p) * pow(n, p - 2)) % p; // 有理数取余
cout << (ans % p + p) % p << endl;
}
}
}