序列消除(erase)
题面
【题目描述】
按如下方式定义一个非空序列 \(b_1,b_2,\ldots,b_m\) 的消除方案:
维护一个由非空区间组成的集合 \(S\)。初始时 \(S=\{[1,m]\}\)。
区间按照二元组 \((l,r)\) 的字典序比较大小。不断进行下列操作,直到 \(S\) 为空:
- 取出 \(S\) 中最小的区间 \([l,r]\),并将其从 \(S\) 中删除;
- 选择一个整数 \(k\),要求在 \(b_l,b_{l+1},\ldots,b_r\) 中至少出现一次 \(k\);
- 将 \([l,r]\) 中所有满足下列条件的非空连续子区间 \([l',r']\) 加入 \(S\):
- 对任意 \(l'\le i\le r'\),均有 \(b_i\ne k\);
- 无法在仍然位于 \([l,r]\) 内的前提下,将 \([l',r']\) 向左或向右扩展并继续满足上一条件。
换言之,在区间 \([l,r]\) 中删除所有值等于 \(k\) 的位置后,将剩余的每个极长连续段加入 \(S\)。
一次消除方案可以用每次操作所选择的 \(k\) 依次组成的序列表示。两个消除方案不同,当且仅当它们对应的选择序列不同。
现在给定一个长度为 \(n\) 的序列 \(a_1,a_2,\ldots,a_n\) 以及 \(q\) 次询问。每次询问给出一个区间 \([l,r]\),你需要求序列 \(a_l,a_{l+1},\ldots,a_r\) 有多少种不同的消除方案。答案可能很大,请对 \(998244353\) 取模。
【样例1输入】
0 2
5 6
3 1 2 3 1
1 1
1 2
1 3
1 4
1 5
2 4
4 3
2 2 2 2
1 4
2 3
4 4
【样例1输出】
1
2
5
6
8
5
1
1
1
【样例1解释】
对于第一组测试数据的第二次询问,子序列为 \((1,2)\)。第一次操作可以选择 \(1\) 或 \(2\),因此共有 \(2\) 种消除方案。
对于只包含同一种数值的非空序列,第一次操作只能选择该数值,随后集合 \(S\) 立即变为空集,因此消除方案数为 \(1\)。
【数据范围与约定】
对于所有测试数据,保证:
- \(1\le T\le 5\)
- \(1\le n,q\le 5\times 10^5\)
- \(1\le a_i\le 20\)
- \(1\le l\le r\le n\)
- 单个输入文件中所有测试数据的 \(n+q\) 之和不超过 \(10^6\)
思路
注意,值域 \(V \le 20\)。
朴素暴力
考虑 \(O(n^2 V^2)\) 朴素暴力,设 \(dp_{c,L,R}\) 表示在区间 \([L,R]\) 中第一步选 \(k=c\) 的方案数,设 \(lst_c\) 为顺序向右搜索时最近的满足 \(a_{lst_c}=c\)。如果在 \([L,R]\) 内没有 \(c\) 则 \(dp_{c,L,R}=0\)。
容易得到转移是:如果全部一样,答案为 \(1\);如果不一样则:
优化
考虑按右端点 \(r\) 扫描线,然后去更新每个 \(dp_c\)。既是我们在到 \(r\) 的时候我们知道了 \(\forall{L},dp_{c,L,r-1}\)。所以可以设新的 \(dp_{c,L}\) 即可。
当 \(a_r \ne c\) 时
\(lst_c\) 不会更新,所以我们发现这相当于把 \(L \from [1,lst_l]\) 的都除掉原来的再乘上新的答案。新的答案就是来自于原式的后半段 \(\sum _{x \ne c} dp_{x,lst_c+1,R}\),所以我们只要整段乘上即可,然后又发现这相当于所有的都乘上(\(0\) 乘完还是 \(0\)),所以用一个标记即可直接维护,新加进去的数直接除以原来的标记即可。
当 \(a_r \ne c\) 时
要加入 \(L \from [lst_c+1,r]\) 的值,注意到这种更新是 \(O(n)\) 次的,所以也可以直接枚举 \(x \ne c\) 暴力更新。
正确代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef long double ld;
#define freop(s) freopen(s".in","r",stdin);freopen(s".out","w",stdout);
#define endl '\n'
#define inf 0x7f7f7f7f
#define lb(x) ((x)&-(x))
#define pii pair<int,int>
#define fir first
#define sec second
#define mkp(a,b) make_pair(a,b)
const int N=5e5+5,M=21,mod=998244353;
int C,T,n,q,a[N],lst[M],l[N],id[M];
ll dp[M][N],mul[M],pre[M],ans[N];
vector<int> qy[N];
inline ll ksm(ll a,ll b=mod-2){
ll c=1;
while(b){
if(b&1) c=c*a%mod;
b>>=1,a=a*a%mod;
}
return c;
}
inline bool cmp(int x,int y){return lst[x]>lst[y];}
inline ll md(ll asd){return asd-(asd>=mod)*mod;}
inline ll ask(int x,int y){return dp[x][y]*mul[x]%mod;}
int main(){
cin.tie(0)->ios::sync_with_stdio(false);
cout.tie(0);
freop("erase");
//↑以上为初始化↑
//------------------------------
cin >> C >> T;
while(T--){
cin >> n >> q,memset(dp,0,sizeof(dp));
for(int i = 1;i<=n;++i)
cin >> a[i],vector<int>().swap(qy[i]);
for(int i = 1;i<M;++i) lst[i]=0,dp[i][0]=pre[i]=mul[i]=1;
for(int i = 1,r;i<=q;++i)
cin >> l[i] >> r,ans[i]=0,
qy[r].push_back(i);
for(int r = 1;r<=n;++r){
ll div=ksm(mul[a[r]]);
for(int l = lst[a[r]]+1;l<r;++l){
ll sum=0;
for(int i = 1;i<M;++i)
sum=md(sum+ask(i,l));
dp[a[r]][l]=div*sum%mod;
}
dp[a[r]][r]=dp[a[r]][r-1],lst[a[r]]=r,pre[a[r]]=mul[a[r]];
for(int i = 1;i<M;++i) id[i]=i;
sort(id+1,id+M,cmp);
for(int ii = 2,i=id[ii];ii<M;i=id[++ii]){
ll sum=0;
for(int jj = 1;jj<ii;++jj)
sum=md(sum+ask(id[jj],lst[i]+1));
mul[i]=pre[i]*sum%mod;
}
for(int id:qy[r])
for(int i = 1;i<M;++i)
ans[id]=md(ans[id]+ask(i,l[id]));
// for(int i = 1;i<9;++i){
// for(int j = 1;j<=r;++j)
// cerr << ask(i,j)%mod << ' ';
// cerr << endl;
// }cerr << endl;
}
for(int i = 1;i<=q;++i)
cout << ans[i] << endl;
}
return 0;
}

浙公网安备 33010602011771号