1 条题解

  • 0
    @ 2025-8-24 22:38:35

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar Anxiomgh
    NOIP 2023 RP++

    搬运于2025-08-24 22:38:35,当前版本为作者最后更新于2022-06-06 21:13:58,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    本蒟蒻不会写 multiset,于是写了一发优先队列(逃)。 如有谬误,大家敬请指正。

    Sol

    首先考虑操作 Query

    g(G1,G2,...Gn)g(G_{1},G_{2},...G_{n}) 表示将 G1,G2,...GnG_{1},G_{2},...G_{n} 以任意方式合并,易得引理 1:

    合并方式 $g(G_{1},G_{2},...G_{i - 1},G_{i+1},...G_{n}) \to G_{i}$ 可以使 GiG_{i} 中的合并后的最大值最小。

    wiw_{i} 为将所有组全部合并到组 GiG_{i} 后的最大值的最小值,viv_{i}GiG_{i} 中原来的的最大值,sumsum 为总糖果数,写成表达式为:

    $$w_{i} = v_{i} + \frac{sum - f_{i}}{\sum_{j = 1}^n{S_{j}}} $$

    可以发现,O(n)O(n) 的预处理即可求得序列 ww。那么原问题就转化成了求 ww 中的最小值和多次 Changeww 中的最小值。

    我们再来研究一下 Change 操作对序列 ww 的影响。

    设共有四个组 G1,G2,G3,G4G_{1},G_{2},G_{3},G_{4},将 G2G_{2} 修改为 G3G_{3}

    • 对于 G1G_{1}G4G_{4},修改操作并不影响它们的最大值,因此 w1w_{1}w4w_{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}}$

    • 对于 G3G_{3},进行分类讨论:

      1. 如果 v2v3v_{2} \leq v_{3},$w_{3}' = v_{3} + \frac{f_{1} + f_{4}}{S_{1} + S_{2}+ S_{3} + S_{4}}$,有 w3<w3w_{3}' < w_{3}

      2. 如果 v2>v3v_{2} > v_{3},$w_{3}' = v_{2} + \frac{f_{1} + f_{4}}{S_{1} + S_{2}+ S_{3} + S_{4}}$,有 w3<w2w_{3}' < w_{2}

    综上所述,可得引理 2:当 GiG_{i} 被修改成 GjG_{j} 时,一定存在:

    wj<wj(vivj)w_{j}' < w_{j}(v_{i} \leq v_{j}) wj<wi(vi>vj)w_{j}' < w_{i}(v_{i} > v_{j})

    这样就可以用优先队列维护了。

    不难发现,对于每一次 Change,都会对应着一个组被删除,一个 vxv_{x} 消失,这个 vxv_{x} 是参与比较的两个最大值中较小的一个。因为在此后任意 GiG_{i} 中的最大值一定不为 vxv_{x}vxv_{x} 就一定不会参与 ww 的计算。建一个数组记录被淘汰的 vxv_{x} 的下标 xx,在优先队列中访问到下标被记录的元素直接 pop 掉即可。

    而对于存留下来的另一个 vyv_{y},它所对应的 Change 前的 ww 虽然在优先队列中也是不合法的,但将新生成的 ww' push 进优先队列,ww' 一定排在它前面,即 w<ww' < w。所以它永远不会被访问到,这就保证了算法的正确性。

    开一个 fafa 数组记录最大值的来源,并不断更新 vv,就可以轻松处理 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;
    		}
    	}
    }
    
    
    • 1

    信息

    ID
    7662
    时间
    2000ms
    内存
    256MiB
    难度
    4
    标签
    递交数
    0
    已通过
    0
    上传者