长度为 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)做如下定义:
- 称区间 [ 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]。
- 区间 [ 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;
}