codeforces1129D Isolation

題面

題意

給出一個長度爲n的序列,將它劃分成一個或多個子串,使每個串中只出現過一次的數字個數小於等於m,求方案個數。

做法

記dp[i]表示前i個的答案,f(i)f(i)表示iinnnn表示此時的右端點)的只出現一次的數字個數,則
dp[n]=f(i)<=mdp[i1]dp[n]=\sum_{f(i)<=m}dp[i-1]
因此只要維護符合條件的dp值之和即可,發現在此時維護的序列後面加上一個數後,會使一個區間的f值+1,一個區間的f值-1,因此可以用分塊進行維護,維護每個塊區間加了多少,此時的答案是多少。

代碼

#include<bits/stdc++.h>
#define ll long long
#define S 410
#define N 100100
#define M 998244353
using namespace std;

ll n,m,s,num[N],ad[S],pos[N],last[N],dp[N],ans[S],cnt[N];
int sum[S][N];

inline ll get(ll u){return u/s+1;}
template<class T,class TT>inline void Add(T &u,TT v){u=((ll)u+v)%M;}

inline void chg(ll u,ll v)
{
    ll t=get(u);
    Add(sum[t][cnt[u]],M-dp[u]);
    if(cnt[u]+ad[t]<=m) Add(ans[t],M-dp[u]);
    cnt[u]+=v;
    Add(sum[t][cnt[u]],dp[u]);
    if(cnt[u]+ad[t]<=m) Add(ans[t],dp[u]);
}

inline void add(ll u,ll v,ll w)
{
    if(u>v) return;
    ll i,p=get(u),q=get(v);
    if(p==q)
    {
	for(i=u;i<=v;i++)
	{
	    chg(i,w);
	}
	return;
    }
    for(i=u;get(i)==p;i++) chg(i,w);
    for(i=v;get(i)==q;i--) chg(i,w);
    for(i=p+1;i<q;i++)
    {
	if(w>0) if(m-ad[i]>=0) Add(ans[i],M-sum[i][m-ad[i]]);
	ad[i]+=w;
	if(w<0) if(m-ad[i]>=0) Add(ans[i],sum[i][m-ad[i]]);
    }
}

int main()
{
    ll i,j,t;
    cin>>n>>m;
    s=sqrt(n);
    for(i=1;i<=n;i++)
    {
	scanf("%lld",&num[i]);
    }
    dp[0]=sum[1][0]=ans[1]=1;
    for(i=1;i<=n;i++)
    {
	last[i]=pos[num[i]];
	add(last[last[i]],last[i]-1,-1);
	add(last[i],i-1,1);
	for(t=get(i),j=i-1;j>=0&&get(j)==t;j--) if(cnt[j]+ad[t]<=m) Add(dp[i],dp[j]);
	for(j=t-1;j>=1;j--) Add(dp[i],ans[j]);
	Add(sum[t][0],dp[i]);
	if(ad[t]<=m) Add(ans[t],dp[i]);
	pos[num[i]]=i;
    }
    cout<<dp[n];
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章