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;
}
后记
题目来源:某周作业
一个很妙的贡献拆分方式。
write on 2025/8/28

浙公网安备 33010602011771号