CF1684F 题解-另类权值拆分

CF1684F Diverse Segments

题目内容

原题
翻译

题目描述

给定长度为 \(n\) 的序列 \(a\),以及 \(m\) 个数对 \((l_i,r_i)\)
你可以进行下列操作至多一次:

  • 选择序列 \(a\) 的一个子段,并将其中的每个元素的值都改成任意整数。

你需要保证执行完操作之后,对于每个整数 \(i(1\leq i\leq m)\),都有 \(a[l_i,r_i]\) 中所有元素互不相同。
你需要最小化操作时选择的子段的长度,并求出这个长度的最小值。
特别的如果没有必要进行操作,答案为 \(0\)

输入格式

第一行输入一个整数 \(t(1\leq t\leq100)\) 表示数据组数,接下来对于每组数据:
第一行输入两个整数 \(n,m(1\leq n,m,\sum n,\sum m\leq2\times10^5)\) 表示序列长度和给定的数对数。
接下来一行输入 \(n\) 个整数表示 \(a_1,a_2,\cdots,a_n(1\leq a_i\leq10^9)\)
接下来输入 \(m\) 行,其中第 \(i\) 行输入两个整数表示 \(l_i,r_i(1\leq l_i\leq r_i\leq n)\)

输出格式

对于每组数据:
输出进行操作的子段的长度的最小值。

题解正文

思路

首先,这种多个区间都要满足不是很好处理,考虑预处理数组 \(mx\) 表示包含 \(i\) 的区间,最后面到哪里,这样将一堆区间转化为对于每个单点的约束,即对于每个 \(i\)\(i\)\(mx_i\) 之间不能有相同颜色。

不难发现,\(mx_i\) 单调不降,而且对于 \(i\)\(mx_i\) 的约束不劣于 \(mx_j (j<i)\) 的限制。也就是说,对于任意 \(i\)\(a_{[i+1,mx_i]}\) 不能有和 \(a_i\) 相同的颜色,是答案合法的充要条件。

注意到对于每个颜色可以分开考虑,不妨设预处理出数组 \(S\) 表示颜色 \(k\) 对应的原数组颜色下标。

对于第 \(S_i\) 个位置,找出最大的 \(j \in S\) 使 \(a_i\)\(a_j\) 必须不相等,即前文的 \(mx_i\)。那么结合上述条件,修改的区间显然需要包含 \(S_t \,(t\in [i,mx_{i}-1])\)\(S_t \,(t\in [i+1,mx_{i}])\)(也就是除去最左边或最右边)

看上去还是一头雾水,怎么办呢?

核心 由于每个颜色可以分开考虑,我们考虑将贡献分开计算

维护 \(mr\) 表示:当 \(L=i\) 时,\(R\) 的最小值。发现存在以下三种情况:

  • 无论如何都有 $R \ge S_{i-1} $,因此 \(f_{1\sim n}\) 都对 \(S_{i-1}\)\(max\)
  • \(L>S_j\) 时,必定有 $R \ge b_i $,因此对 \(f_{b_{j+1}\sim n}\) 都对 \(S_{i}\)\(max\)
  • \(L>S_{j+1}\) 时,一定无解,因此对 \(f_{b_{j+1}+1\sim n}\) 都对 \(\infin\)\(max\)

注意到,上述方案要对区间取 \(max\),显然可以线段树,但我太懒了。注意到,上述问题全部都是 \(f_{i \sim n}\)\(max\)。不妨考虑类似于前缀和的东西,每次只在 \(i\)\(max\),然后在最后从 \(1\sim n\) 取前缀 \(max\)

代码

//The sunshine cast a blush on her face, and adorn the sparkling corners of her eyes with a rainbow.
#include<bits/stdc++.h>
using namespace std;
#define LL long long
const int N=2e5+10,Mod=998244353;
const int INF=0x3f3f3f3f;
//const LL INF=0x3f3f3f3f3f3f3f3f;
int n,m,k;

template <typename T> void read(T &a){
	char c=getchar();T w=1,f=0;while(c<'0'||c>'9'){if(c=='-') w=-w;c=getchar();}
	while(c>='0'&&c<='9') f=f*10+c-'0',c=getchar();a=f*w;
}
template <typename T> void tomin(T &a,const T &b){if(b<a) a=b;}
template <typename T> void tomax(T &a,const T &b){if(a<b) a=b;}
void modadd(int &a,const int &b,const int &mod=Mod){a+=b;if(a<0) a+=mod;if(a>=mod) a-=mod;}
void modsub(int &a,const int &b,const int &mod=Mod){a-=b;if(a<0) a+=mod;if(a>=mod) a-=mod;}
void modmul(int &a,const int &b,const int &mod=Mod){a=1ll*a*b%mod;}

map <int,int> mp;
int lsh(int a[],int n)
{
	mp.clear();int lscnt=0;
	for(int i=1;i<=n;i++)
	{
		if(!mp.count(a[i])) mp[a[i]]=++lscnt;
		a[i]=mp[a[i]];
	}
	return lscnt;
}

int a[N];
vector <int> v[N];
int mx[N],mr[N];

int T=1;
void __solve()
{
	read(n);read(m);
	memset(mx,0,sizeof(mx));memset(mr,0,sizeof(mr));
	for(int i=1;i<=n;i++) read(a[i]),v[i].clear();
	k=lsh(a,n);
	for(int i=1;i<=n;i++) v[a[i]].push_back(i);
	for(int i=1;i<=m;i++)
	{
		int l,r;
		read(l);read(r);
		tomax(mx[l],r);
	}
	for(int i=2;i<=n;i++) tomax(mx[i],mx[i-1]);
	for(int i=1;i<=k;i++)
	{
		int l=0;
		for(int j=0;j<v[i].size();j++)
		{
			while(l<j&&mx[v[i][l]]<v[i][j]) l++;
			if(l==j||mx[v[i][l]]<v[i][j]) continue;
			tomax(mr[0],v[i][j-1]);
			tomax(mr[v[i][l]+1],v[i][j]);
			tomax(mr[v[i][l+1]+1],INF);
		}
	}
	int res=INF;
	for(int i=1;i<=n;i++)
	{
		tomax(mr[i],max(mr[i-1],i-1));
		if(mr[i]>=INF) break;
		tomin(res,mr[i]-i+1);
	}
	printf("%d\n",res);
}
int main()
{
	cin>>T;
	while(T--) __solve();
	return 0;
}

后记

题目来源:某周作业

参考资料1
参考资料2

一个很妙的贡献拆分方式。

write on 2025/8/28

posted @ 2026-01-18 11:29  FarrisL  阅读(2)  评论(0)    收藏  举报