1 条题解

  • 0
    @ 2025-8-24 23:16:31

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar Chenhaoxuan
    **

    搬运于2025-08-24 23:16:31,当前版本为作者最后更新于2025-05-30 19:54:33,作者可能在搬运后再次修改,您可在原文处查看最新版

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

    以下是正文


    P12568 [UOI 2023] An Array and Range Additions

    题目大意:

    给定一个长度为 nn 的整数数组 aa。每次操作可以选择一个区间加上任意整数。求使数组 aa 中所有元素两两不同的最小操作次数。


    首先我们先考虑本题的弱化版本:你需要保证 选择的区间两两不交

    这时问题变成了一个简单的贪心。我们从左至右扫描整个序列,每次遇到一个重复的数字,就把序列从这里断开,然后新开一段。最后,我们保留第一段不变,后面每一段加上一个互不相同的数字。容易证明最优性。


    如果区间可以重叠呢?我们先钦定 序列中的所有数字必须至少改变一次,考虑如下 n=3n=3 的情形:

    11

    容易发现如下的操作方案:

    x+1x+1 x+y+1x+y+1 y+1y+1

    其中 xx 远大于 aia_iyy 远大于 xx

    考虑如下 n=5n=5 的情形:

    11

    下面是一种合理的操作方案:

    x+1x+1 x+y+1x+y+1 x+y+z+1x+y+z+1 y+z+1y+z+1 z+1z+1

    其中 xx 远大于 aia_iyy 远大于 xxzz 远大于 yy

    由此推广,我们可以得到如下一般结论:

    • 对于 nn 次操作,我们最多让 2n12n-1 个区间变得不同。

      • 具体实现上,让每次操作依次覆盖 [1,n],[2,n+1],,[n,2n1][1,n],[2,n+1],\cdots, [n,2n-1] 这些区间即可。
    • 对于 nn 个区间,我们至少进行 n2+1\left\lfloor\dfrac{n}{2}\right\rfloor+1 次操作才能让它们两两不同。


    现在考虑更一般的情形。我们可能会面对答案由若干段的改变构成的情况。下面是一个例子。

    原序列 11 33 22 11 22 33
    答案序列 11 x+1x+1 x+3x+3 x+2x+2 22 y+1y+1 y+2y+2 y+3y+3 33

    这样的答案很难求解。但我们发现:我们可以把两个相邻的两个操作区间合并,如下所示:

    原序列 11 33 22 11 22 33
    新答案序列 11 x+1x+1 x+3x+3 x+2x+2 x+y+2x+y+2 y+1y+1 y+2y+2 y+3y+3 33

    把中间的无关数字也“带上”操作,容易发现这样的构造也符合要求。于是存在一种合法的构造方案,只有最前面和最后面的一些位置不改变,中间的数字都要改变。


    回到本题。我们首先预处理出 每一个点右侧第一次出现重复数字的位置,记为 nxtinxt_i。容易以线性对数的复杂度求出。

    然后,我们对于每一个合法的前缀 [1,l][1,l],找出一个极长的合法后缀 [r,n][r,n]。这可以双指针动态维护,总情况数不超过 nn。我们令这些数字不变。

    剩下的就只有求出 [l+1,r1][l+1,r-1] 中的最少操作数。这转化为上面 序列中的所有数字必须至少改变一次 的情况。由于我们已经求出了 nxtnxt 数组,我们可以从 l+1l+1 开始,在 nxtnxt 上暴力往后跳直至超过 r1r-1,这样单次查询复杂度是 O(n)\mathcal O(n) 的。

    而上面的跳跃操作是可以倍增优化的,于是我们可以在 O(nlogn)\mathcal O(n\log n) 的时间复杂度内求出每对前后缀的答案。把所有情况的答案取最小值即可。

    注意特判不需要操作的情形。


    AC 代码:

    int n, a[maxn], cnt;
    int nxt[maxn][20], ans = 0x3f3f3f3f;
    map<int, int> mp;
    int query(int l, int r) {
    	int res = 1;
    	for (int j = 19; j >= 0; j--) {
    		if (nxt[l][j] <= r) l = nxt[l][j], res += (1 << j);
    	}
    	return res / 2 + 1;
    }
    int main() {
    	scanf("%d", &n);
    	for (int i = 1; i <= n; i++) {
    		scanf("%d", &a[i]);
    	}
    	nxt[n + 1][0] = n + 1;
    	for (int i = n; i >= 1; i--) {
    		nxt[i][0] = nxt[i + 1][0];
    		if (mp[a[i]]) nxt[i][0] = min(nxt[i][0], mp[a[i]]);
    		mp[a[i]] = i;
    	}
    	for (int i = 1; i <= 19; i++) {
    		for (int j = 1; j <= n + 1; j++) 
    			nxt[j][i] = nxt[nxt[j][i - 1]][i - 1];
    	}
    	mp.clear(); int l, r;
    	for (r = n; r >= 1; r--) {
    		if (!mp[a[r]]) mp[a[r]]++;
    		else break;
    	}
    	if (!r) return 0 * puts("0");
    	ans = min(ans, query(1, r)); r++;
    	for (l = 1; l <= n; l++) {
    		while (mp[a[l]] && r <= n) {
    			mp[a[r]]--; r++;
    		}
    		if (mp[a[l]]) break;
    		mp[a[l]] = 1;
    		ans = min(ans, query(l + 1, r - 1));
    	}
    	printf("%d", ans);
    	return 0;
    }
    
    • 1

    信息

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