题目链接
题目解法
先观察满足条件的一段长什么样
- 长度必须是偶数
- 不存在绝对众数
如果不存在绝对众数,那么一定可以每次删去一个出现次数最多的数,以此类推,一定可以删完
如果存在绝对众数,就算每次会删掉一个绝对众数和一个其他数,绝对众数一定会剩下几个删不了
所以这可以写出一个朴素的
O
(
n
2
)
D
P
O(n^2)\;DP
O(n2)DP
即
d
p
[
i
]
=
∑
a
j
.
.
.
a
i
中不存在绝对众数
d
p
[
j
−
1
]
dp[i]=\sum_{a_j...a_i中不存在绝对众数}{dp[j-1]}
dp[i]=∑aj...ai中不存在绝对众数dp[j−1]
考虑优化
绝对众数的第一反映应该是摩尔投票
根据摩尔投票的可结合性,把区间分成
2
2
2 段,必有一段的绝对众数是总区间的绝对众数
考虑
c
d
q
cdq
cdq 分治
对于
m
i
d
+
1
−
r
mid+1-r
mid+1−r 的
d
p
dp
dp,只需考虑跨过
m
i
d
mid
mid 的区间
对于一个长度为
l
e
n
len
len 的数列,每一个前缀的绝对众数的不可重集大小一定只有
O
(
l
o
g
l
e
n
)
O(log \;len)
O(loglen) 个,因为考虑最可观的情况是后一半全是一个数,然后前一半的后一半全是一个数,以此类推,这样的众数是最多的,有
O
(
l
o
g
l
e
n
)
O(log \;len)
O(loglen) 个
所以只要对每一个前缀后缀中的众数做一遍 d p dp dp 就可以了,需要用后缀和优化及用偏移量 D e l t a Delta Delta 使数组为正
#include <bits/stdc++.h>
using namespace std;
const int N(1000100),P(998244353);
int n,a[N],dp[N];
int cnt[N],sum[N<<1];
inline int read(){
int FF=0,RR=1;
char ch=getchar();
for(;!isdigit(ch);ch=getchar()) if(ch=='-') RR=-1;
for(;isdigit(ch);ch=getchar()) FF=(FF<<1)+(FF<<3)+ch-48;
return FF*RR;
}
void solve(int l,int r){
if(l==r) return;
int mid=(l+r)>>1;
solve(l,mid);
int s=0;
for(int i=l;i<=mid;i++) (s+=dp[i])%=P;
for(int i=mid+1;i<=r;i++) (dp[i]+=s)%=P;
//求mid+1 - r 的前缀众数集合
set<int> mode;
for(int i=mid+1;i<=r;i++){
cnt[a[i*2-1]]++,cnt[a[i*2]]++;
if(cnt[a[i*2-1]]*2>i-mid) mode.insert(a[i*2-1]);
if(cnt[a[i*2]]*2>i-mid) mode.insert(a[i*2]);
}
for(int i=mid+1;i<=r;i++) cnt[a[i*2-1]]=cnt[a[i*2]]=0;
//求l - mid 的后缀众数集合
for(int i=mid;i>=l;i--){
cnt[a[i*2-1]]++,cnt[a[i*2]]++;
if(cnt[a[i*2-1]]*2>mid-i+1) mode.insert(a[i*2-1]);
if(cnt[a[i*2]]*2>mid-i+1) mode.insert(a[i*2]);
}
for(int i=mid;i>=l;i--) cnt[a[i*2-1]]=cnt[a[i*2]]=0;
//计算出 mid+1 - r 的跨过mid的dp值
int Delta=(r-l+1)<<1;
for(set<int>::iterator it=mode.begin();it!=mode.end();it++){
int v=*it;
for(int i=mid,s=0;i>l;i--){
s+=a[i*2-1]==v?1:-1,s+=a[i*2]==v?1:-1;
(sum[s+Delta]+=dp[i-1])%=P;
}
(sum[Delta]+=dp[mid])%=P;
for(int i=Delta<<1;i>=0;i--) (sum[i]+=sum[i+1])%=P;
for(int i=mid+1,s=0;i<=r;i++){
s+=a[i*2-1]==v?1:-1,s+=a[i*2]==v?1:-1;
dp[i]=(dp[i]-sum[-s+1+Delta]+P)%P;
}
for(int i=0;i<=Delta<<1;i++) sum[i]=0;
}
solve(mid+1,r);
}
int main(){
n=read();
for(int i=1;i<=n<<1;i++) a[i]=read();
dp[0]=1;
solve(0,n);
printf("%d",dp[n]);
return 0;
}