把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

【CF1129D】Isolation(分块优化DP)

题目链接

  • 给定一个长度为 \(n\) 的序列,求有多少种方案将它划分成若干段,使得每一段中出现恰好一次的元素不超过 \(k\) 个。
  • \(1\le n\le10^5\)

分块优化 DP

容易想到设 \(f_i\)表示以 \(i\) 为一段结尾时的答案。

那么从 \(j\) 能转移到 \(i\),根据题目的要求,充要条件是 \((j,i]\) 中出现恰好一次的元素个数不超过 \(k\)

由于我们是在从小到大枚举 \(i\) 转移的,因此只要考虑如何在这一过程中维护 \((j,i]\) 中出现恰好一次的元素个数,即每次考虑 \(a_i\) 对于这一数值的影响。

所以找到 \(a_i\) 上次出现的位置 \(p1\) 和上上次出现的位置 \(p2\),则对 \(j\in[p2,p1)\)\((j,i]\) 中出现恰好一次的元素个数减少 \(1\),对 \(j\in[p1,i)\)\((j,i]\) 中出现恰好一次的元素个数增加 \(1\),其余位置不受影响。

一般的数据结构难以维护这种东西,因此考虑分块。具体地,散块修改直接暴力重构,整块修改只要用桶存一下出现恰好一次的元素个数为每种数值的 \(f_i\) 之和,就可以快速更新信息了。

代码:\(O(n\sqrt n)\)

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Rg register
#define RI Rg int
#define Cn const
#define CI Cn int&
#define I inline
#define W while
#define N 100000
#define SN 320
#define INF 1e9
#define X 998244353
#define Inc(x,y) ((x+=(y))>=X&&(x-=X))
#define Dec(x,y) ((x-=(y))<0&&(x+=X))
using namespace std;
namespace FastIO
{
	#define FS 100000
	#define tc() (FA==FB&&(FB=(FA=FI)+fread(FI,1,FS,stdin),FA==FB)?EOF:*FA++)
	char oc,FI[FS],*FA=FI,*FB=FI;
	Tp I void read(Ty& x) {x=0;W(!isdigit(oc=tc()));W(x=(x<<3)+(x<<1)+(oc&15),isdigit(oc=tc()));}
	Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
}using namespace FastIO;
int n,k,f[N+5],p1[N+5],p2[N+5];
namespace Block//分块
{
	int sz,bl[N+5];I void Bd() {sz=sqrt(n);for(RI i=0;i<=n;++i) bl[i]=i/sz+1;}
	int tot,c[N+5],tg[SN],g[SN][N+5];I void BF(CI l,CI r,CI v)//暴力修改
	{
		RI i,o=bl[l];for(i=(o-1)*sz;i<=min(o*sz-1,n);++i) c[i]+tg[o]<=k&&Dec(tot,f[i]),g[o][c[i]]=0;for(i=l;i<=r;++i) c[i]+=v;//删除原有信息并修改
		for(i=(o-1)*sz;i<=min(o*sz-1,n);++i) (c[i]+=tg[o])<=k&&Inc(tot,f[i]),Inc(g[o][c[i]],f[i]);tg[o]=0;//加入新信息
	}
	I void U(CI l,CI r,CI v)//修改
	{
		RI L=bl[l],R=bl[r];if(L==R) return (void)BF(l,r,v);BF(l,L*sz-1,v),BF((R-1)*sz,r,v);//散块暴力
		for(RI i=L+1;i<R;++i) tg[i]+=v,~v?k+1-tg[i]>=0&&Dec(tot,g[i][k+1-tg[i]]):k-tg[i]>=0&&Inc(tot,g[i][k-tg[i]]);//整块利用维护出的桶快速更新信息
	}
	I void Ins(CI x) {c[x]<=k&&Inc(tot,f[x]),Inc(g[bl[x]][c[x]],f[x]);}//添加一个新位置
}using namespace Block;
int main()
{
	RI i,x;for(read(n,k),Bd(),f[0]=1,Ins(0),i=1;i<=n;++i)
		read(x),p1[x]&&(U(p2[x],p1[x]-1,-1),p2[x]=p1[x]),U(p1[x],i-1,1),p1[x]=i,f[i]=tot,Ins(i);//修改后转移
	return printf("%d\n",f[n]),0;
}
posted @ 2022-01-28 15:21  TheLostWeak  阅读(160)  评论(0编辑  收藏  举报