一类判断包含颜色整体的题目

长度为 n n n 的颜色序列 { c } \{c\} {c},称区间 [ l , r ] [l,r] [l,r] 合法,当且仅当不存在一种颜色 x x x 在区间内外均出现过。

遇到这种题目,发现内外有冲突可以用异或相消的性质,又想到 Lampice 这道题,考虑一个随机赋权。

对于某种颜色 x x x 的所有坐标 p o s x , i pos_{x,i} posx,i,我们给除了最后一个之外的其他坐标都赋不同的权值,最后一个赋其他坐标的权值异或和,这样只有一个区间内选到所有颜色 x x x 的坐标,异或和才为 0 0 0。

cpp 复制代码
for(int i=1;i<=n;i++)
{
	scanf("%lld",&c[i]);
	cnt[c[i]]++;
	hs[i]=cox[i]=0;
}
for(int i=1;i<=n;i++)
{
	cnt[c[i]]--;
	if(!cnt[c[i]])hs[i]=cox[c[i]];
	else hs[i]=rnd(),cox[c[i]]^=hs[i];
}

1.洛谷 P4065 JXOI2017 颜色

题意

可怜有一个长度为 n n n 的正整数序列 A i A_i Ai,其中相同的正整数代表着相同的颜色。

现在可怜觉得这个序列太长了,于是她决定选择一些颜色把这些颜色的所有位置都删去。

删除颜色 i i i 可以定义为把所有满足 A j = i A_j = i Aj=i 的位置 j j j 都从序列中删去。

然而有些时候删去之后,整个序列变成了好几段,可怜不喜欢这样,于是她想要知道有多少种删去颜色的方案使得最后剩下来的序列非空且连续。

例如颜色序列 { 1 , 2 , 3 , 4 , 5 } \{1, 2, 3, 4, 5\} {1,2,3,4,5},删除颜色 3 3 3 后序列变成了 { 1 , 2 } \{1, 2\} {1,2} 和 { 4 , 5 } \{4, 5\} {4,5} 两段,不满足条件。而删除颜色 1 1 1 后序列变成了 { 2 , 3 , 4 , 5 } \{2, 3, 4, 5\} {2,3,4,5},满足条件。

两个方案不同当且仅当至少存在一个颜色 i i i 只在其中一个方案中被删去。

1 ≤ T , ∑ n ≤ 3 × 1 0 5 , 1 ≤ A i ≤ n 1 \le T,\sum n \le 3 \times 10^5, 1 \le A_i \le n 1≤T,∑n≤3×105,1≤Ai≤n。

思路

根据上面的思路,转化为异或和为 0 0 0 的区间个数。枚举右端点即可。

代码

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll N=3e5+9;
ll Q,n,c[N];
ll cnt[N];
//mt19937 rnd(time(0));
//悲报:mt19937被卡了
ll rnd()
{
	return (ll)rand()<<30|rand();
}
ll hs[N],cox[N],sx[N];
unordered_map<ll,ll>mp;
int main()
{
	srand(time(0));
	scanf("%lld",&Q);
	while(Q--)
	{
		mp.clear();
		scanf("%lld",&n);
		for(int i=1;i<=n;i++)
		{
			scanf("%lld",&c[i]);
			cnt[c[i]]++;
			hs[i]=cox[i]=0;
		}
		for(int i=1;i<=n;i++)
		{
			cnt[c[i]]--;
			if(!cnt[c[i]])hs[i]=cox[c[i]];
			else hs[i]=rnd(),cox[c[i]]^=hs[i];
		}
		for(int i=1;i<=n;i++)
		sx[i]=sx[i-1]^hs[i];
		ll ans=0;
		mp[0]=1;
		for(int i=1;i<=n;i++)
		{
			ans+=mp[sx[i]];
			mp[sx[i]]++;
		}
		printf("%lld\n",ans);
	}
	return 0;
}

2.洛谷 P14567 区间

题意

P14567 【MX-S12-T2】区间

题目描述

给定三个长度为 n n n 的正整数序列:颜色序列 c c c,权值序列 v v v,代价序列 f f f。序列的下标均由 1 1 1 开始标号。保证代价序列单调不减 ,即 f i ≤ f i + 1 f_i \le f_{i + 1} fi≤fi+1。

对一个区间 [ l , r ] [l, r] [l,r]( 1 ≤ l ≤ r ≤ n 1 \le l \le r \le n 1≤l≤r≤n)做如下定义:

  1. 称区间 [ l , r ] [l, r] [l,r] 合法 ,当且仅当:不存在一种颜色 x x x 在区间内外均出现过,即不存在颜色 x x x 和下标 i , j i, j i,j 满足 c i = c j = x c_i = c_j = x ci=cj=x 且 i ∈ [ l , r ] i \in [l, r] i∈[l,r]、 j ∉ [ l , r ] j \notin [l, r] j∈/[l,r]。
  2. 区间 [ l , r ] [l, r] [l,r] 的价值 定义为 ∑ i = l r ( v i ⋅ f i − l + 1 ) \displaystyle \sum_{i = l}^{r} (v_i \cdot f_{i - l + 1}) i=l∑r(vi⋅fi−l+1)。

找出一个价值最小的合法区间,你只需要求出该最小价值。

1 ≤ n ≤ 1 0 6 1 \le n \le 10^6 1≤n≤106, 1 ≤ f i ≤ 1 0 3 1 \le f_i \le 10^3 1≤fi≤103,且 f f f 序列单调不减; 1 ≤ v i ≤ 1 0 3 1 \le v_i \le 10^3 1≤vi≤103; 1 ≤ c i ≤ n 1 \le c_i \le n 1≤ci≤n。

思路

用上面的方法, O ( n ) O(n) O(n) 预处理就可以 O ( 1 ) O(1) O(1) 判断区间 [ l , r ] [l,r] [l,r] 是否合法。

假设有 [ x , y ] [x,y] [x,y] 和 [ y + 1 , z ] [y+1,z] [y+1,z] 合法,计算 [ x , z ] [x,z] [x,z] 的贡献必然劣于二者其中之一。所以对于右端点 i i i,找到前面离它最近的 h c j = h c i hc_j=hc_i hcj=hci,计算 [ i , j ] [i,j] [i,j] 的贡献即可。时间复杂度 O ( n + ? ) O(n+?) O(n+?),取决于颜色的离散程度。不过最慢的点只跑了 436ms。

代码

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=1e6+9,mod=1e9+7;
const ll inf=1e18;
inline int read()
{
   int s=0,w=1;
   char ch=getchar();
   while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
   while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
   return s*w;
}
inline void write(ll x)
{ 
    if(x==0){putchar('0');return;}
	ll len=0,k1=x,c[10005];
	if(k1<0)k1=-k1,putchar('-');
	while(k1)c[len++]=k1%10+'0',k1/=10;
	while(len--)putchar(c[len]);
}
bool mst;
int n,c[N],v[N],f[N];
mt19937 rnd(time(0));
int cmx,vmx,fmx;
int cnt[N];
int val[N];
bool vis[N];
ll sf[N],sv[N];
ll hs[N],cox[N],sx[N];
unordered_map<ll,int>pos;
int jest[N],cr[N];
bool med;
int main()
{
	n=read();
	for(int i=1;i<=n;i++)
	{
		c[i]=read();
		cmx=max(cmx,c[i]);
		cnt[c[i]]++;
	}
	for(int i=n;i>=1;i--)
	{
		if(!vis[c[i]])cr[c[i]]=i;
		vis[c[i]]=1;
	}
	for(int i=1;i<=n;i++)//fuquan 
	{
		cnt[c[i]]--;
		if(!cnt[c[i]])hs[i]=cox[c[i]];
		else 
		{
			hs[i]=rnd();
			cox[c[i]]^=hs[i];
		}
	}
	for(int i=1;i<=n;i++)
	sx[i]=sx[i-1]^hs[i];
//	getval();
	for(int i=1;i<=n;i++)
	{
		v[i]=read();
		sv[i]=sv[i-1]+v[i];
		vmx=max(vmx,v[i]);
	}
	for(int i=1;i<=n;i++)
	{
		f[i]=read();
		sf[i]=sf[i-1]+f[i];
		fmx=max(vmx,v[i]);
	}
	memset(jest,-1,sizeof(jest));
	for(int i=1;i<=n;i++)
	{
		int l1=pos[sx[i]];
		pos[sx[i]]=i;
		if(sx[i]==sx[l1])jest[i]=l1;
	}
	ll mi=inf;
//	cout<<n<<" "<<cmx<<" "; 
	for(int i=1;i<=cmx;i++)//sub1
	{
		int id=cr[i];
		ll ret=0;
		if(jest[id]<0)continue;
		if(fmx==1)//sub2
		{
			mi=min(mi,sv[id]-sv[jest[id]]);
			continue;
		}
		if(vmx==1)//sub4
		{
			mi=min(mi,sf[id-jest[id]]);
			continue;
		}
		for(int j=jest[id]+1;j<=id;j++)
		ret+=v[j]*f[j-jest[id]];
		mi=min(mi,ret);
	}
	write(mi);
	return 0;
}
相关推荐
Demon--hx1 小时前
[c++]string的三种遍历方式
开发语言·c++·算法
无敌最俊朗@1 小时前
力扣hot100 - 合并两个有序链表21
算法·leetcode·链表
墨染点香1 小时前
LeetCode 刷题【168. Excel 表列名称】
算法·leetcode·职场和发展
hans汉斯1 小时前
基于改进YOLOv11n的无人机红外目标检测算法
大数据·数据库·人工智能·算法·yolo·目标检测·无人机
Swift社区2 小时前
LeetCode 431 - 将 N 叉树编码成二叉树
算法·leetcode·职场和发展
子豪-中国机器人2 小时前
1030-csp 2019 入门级第一轮
算法
关注我立刻回关3 小时前
洛谷平台
算法
Cx330❀3 小时前
C++ map 全面解析:从基础用法到实战技巧
开发语言·c++·算法
CS_浮鱼3 小时前
【Linux】线程
linux·c++·算法