Codeforces 题目传送门 & 洛谷题目传送门

又独立切了道 *2900(

首先考虑 \(dp\),\(dp_i\) 表示以 \(i\) 为结尾的划分的方式,那么显然有转移 \(dp_i=\sum\limits_{j=0}^{i-1}dp_j[uni(j+1,i)\le k]\),其中 \(uni(l,r)\) 表示 \([l,r]\) 中出现恰好一次的数的个数。

暴力一脸过不去,考虑优化。我们实时维护一个数组 \(f_j=uni(j+1,i)\),那么上式可写作 \(dp_i=\sum\limits_{j=0}^{i-1}dp_j[f_j\le k]\),而注意到每次 \(i\) 指针的右移引起 \(f\) 数组的变化应为两个区间的 \(+1/-1\),具体来说,我们记 \(pre_v\) 表示 \(v\) 上一次出现的位置,\(pre\_pre_v\) 为 \(v\) 上上次出现的位置,那么指针移到 \(i\) 时 \(f\) 的变化应为 \([pre\_pre_{a_i},pre_{a_i})\) 内的 \(f_j\) 减 \(1\),\([pre_{a_i},i)\) 内的 \(f_j\) 加 \(1\),这个很容易想通。

于是现在问题就转化为,有一个序列,每个元素都有一个权值 \(\text{value}\) 和键值 \(\text{key}\),每次我们可以将一个区间的键值 \(+1/-1\) 或者询问序列中键值 \(\le k\) 的元素的权值之和,注意到这东西既涉及下标又涉及值,是不太好用常规的数据结构,如线段树/平衡树之类维护的,因此考虑硬核数据结构——分块。我们设一个阈值 \(B\) 并将原序列分成 \(B\) 块,并对每一块建一个 BIT,每次我们执行区间 \(+v\) 时,边角块就暴力修改 \(f_i\) 并修改 BIT 里的值,中间块就维护一个标记数组 \(tag_i\) 并令 \(tag_i\) 加上 \(v\),查询时就枚举每一块,并在 BIT 中查询 \(\le k-tag_i\) 所有数的和即可。时间复杂度 \(n\sqrt{n\log n}\),不知道能不能过?

考虑优化,注意到上面的解法并没有用到每次加的值为 \(\pm 1\) 这一性质,所以使用了 BIT 也就多了一个 \(\sqrt{\log n}\) 的复杂度。我们考虑直接对于每一块维护一个前缀和数组 \(sum_{i,j}\) 表示第 \(i\) 块中 \(f_k\le j\) 的 \(dp_k\) 之和,那么由于加的值为 \(\pm 1\),故每次对区间中一个元素暴力加值的时候,最多会引起一个 \(sum_{i,j}\) 的变化,具体来说,如果我们令 \(f_j\) 加上 \(1\),那么对应的 \(sum_{i,f_j}\) 会减去 \(dp_j\);如果我们令 \(f_j\) 减去 \(1\),那么对应的 \(sum_{i,f_j-1}\) 会加上 \(dp_j\);因此修改单个元素的复杂度就变成了 \(\mathcal O(1)\),总复杂度也就变成了 \(n\sqrt{n}\)

还有一个小小的问题,就是当我们计算出 \(dp_i\) 之后,对应的 \(f_i\) 显然是 \(0\),而此时该位置的权值也发生了改变,即由 \(0\) 变为了 \(dp_i\),此时我们就要遍历 \(j\in[0,n]\) 并令 \(sum_{b,j}\) 加上 \(dp_i\),其中 \(b\) 为 \(i\) 所在的块,这样复杂度又会退化到 \(n^2\),这显然是我们所不愿意看到的。不过解决方法非常 simple,我们只需将 \(sum_{i,j}\) 的定义改为第 \(i\) 块中 \(f_k\ge j\) 的 \(dp_k\) 之和,查询时维护一个 \(tot_i\) 表示这块中所有的 \(dp_j\) 之和,拿这块中所有的 \(dp_j\) 之和减去不合法的情况即可,这样修改还是 \(\mathcal O(1)\) 的,不过最后求完 \(dp_i\) 之后的遍历过程就没了,取而代之的是我们只用令 \(sum_{b,0}\) 加上 \(dp_i\),这样复杂度就是货真价实的 \(n\sqrt{n}\) 辣(

代码非常好写,不过似乎有些卡空间(?),而且我的似乎跑得挺快?最慢的一个点才跑了 249ms

const int MAXN=1e5;
const int SQRT=316;
const int MOD=998244353;
void add(int &x,int y){((x+=y)>=MOD)&&(x-=MOD);}
int n,k,a[MAXN+5],blk_sz,blk_cnt,bel[MAXN+5];
int L[SQRT+5],R[SQRT+5],sum[SQRT+5][MAXN+5];
int cnt[MAXN+5],tag[SQRT+5],dp[MAXN+5],tot[SQRT+5];
int pre[MAXN+5],pre_pre[MAXN+5];
void add(int x,int y,int z){
if(x>y) return;
if(bel[x]==bel[y]){
for(int i=x;i<=y;i++){
if(z==1) add(sum[bel[x]][++cnt[i]],dp[i]);
else add(sum[bel[x]][cnt[i]--],MOD-dp[i]);
}
} else {
for(int i=x;i<=R[bel[x]];i++){
if(z==1) add(sum[bel[x]][++cnt[i]],dp[i]);
else add(sum[bel[x]][cnt[i]--],MOD-dp[i]);
}
for(int i=L[bel[y]];i<=y;i++){
if(z==1) add(sum[bel[y]][++cnt[i]],dp[i]);
else add(sum[bel[y]][cnt[i]--],MOD-dp[i]);
}
for(int i=bel[x]+1;i<bel[y];i++) tag[i]+=z;
}
}
int calc(){
int ret=0;
for(int i=0;i<=blk_cnt;i++){
add(ret,tot[i]);
if(k-tag[i]+1>=0) add(ret,MOD-sum[i][k-tag[i]+1]);
else add(ret,MOD-tot[i]);
} return ret;
}
int main(){
scanf("%d%d",&n,&k);dp[0]=1;
blk_sz=(int)pow(n,0.5);blk_cnt=n/blk_sz+1;
for(int i=1;i<=blk_cnt;i++){
L[i]=(i-1)*blk_sz;R[i]=min(i*blk_sz-1,n);
for(int j=L[i];j<=R[i];j++) bel[j]=i;
} sum[1][0]=1;tot[1]=1;
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<=n;i++){
add(pre_pre[a[i]],pre[a[i]]-1,-1);add(pre[a[i]],i-1,1);
pre_pre[a[i]]=pre[a[i]];pre[a[i]]=i;dp[i]=calc();
add(sum[bel[i]][0],dp[i]);add(tot[bel[i]],dp[i]);
// printf("%d\n",dp[i]);
} printf("%d\n",dp[n]);
return 0;
}

Codeforces 1129D - Isolation(分块优化 dp)的更多相关文章

  1. D - The Bakery CodeForces - 834D 线段树优化dp···

    D - The Bakery CodeForces - 834D 这个题目好难啊,我理解了好久,都没有怎么理解好, 这种线段树优化dp,感觉还是很难的. 直接说思路吧,说不清楚就看代码吧. 这个题目转 ...

  2. Consecutive Subsequence CodeForces - 977F (map优化DP)·

    You are given an integer array of length nn. You have to choose some subsequence of this array of ma ...

  3. Linear Kingdom Races CodeForces - 115E (线段树优化dp)

    大意: n条赛道, 初始全坏, 修复第$i$条花费$a_i$, m场比赛, 第$i$场比赛需要占用$[l_i,r_i]$的所有赛道, 收益为$w_i$, 求一个比赛方案使得收益最大. 设$dp[i]$ ...

  4. New task CodeForces - 788E (线段树优化dp)

    比较套路的一个题, 对每个数维护一颗线段树来转移就好了. #include <iostream> #include <algorithm> #include <cstdi ...

  5. codeforces 939F 单调队列优化dp

    F. Cutlet time limit per test 4 seconds memory limit per test 256 megabytes input standard input out ...

  6. [BZOJ4850][JSOI2016]灯塔(分块/决策单调性优化DP)

    第一种方法是决策单调性优化DP. 决策单调性是指,设i>j,若在某个位置x(x>i)上,决策i比决策j优,那么在x以后的位置上i都一定比j优. 根号函数是一个典型的具有决策单调性的函数,由 ...

  7. Codeforces 946G Almost Increasing Array (树状数组优化DP)

    题目链接   Educational Codeforces Round 39 Problem G 题意  给定一个序列,求把他变成Almost Increasing Array需要改变的最小元素个数. ...

  8. Codeforces 1603D - Artistic Partition(莫反+线段树优化 dp)

    Codeforces 题面传送门 & 洛谷题面传送门 学 whk 时比较无聊开了道题做做发现是道神题( 介绍一种不太一样的做法,不观察出决策单调性也可以做. 首先一个很 trivial 的 o ...

  9. Codeforces Round #426 (Div. 2) D 线段树优化dp

    D. The Bakery time limit per test 2.5 seconds memory limit per test 256 megabytes input standard inp ...

随机推荐

  1. netty系列之:使用netty实现支持http2的服务器

    目录 简介 基本流程 CleartextHttp2ServerUpgradeHandler Http2ConnectionHandler 总结 简介 上一篇文章中,我们提到了如何在netty中配置TL ...

  2. Hive面试题整理(一)

    1.Hive表关联查询,如何解决数据倾斜的问题?(☆☆☆☆☆)   1)倾斜原因:map输出数据按key Hash的分配到reduce中,由于key分布不均匀.业务数据本身的特.建表时考虑不周.等原因 ...

  3. VS Code C/C++开发环境配置

    VS Code C/C++开发环境配置 一.安装 ​ 1.前往官网下载安装即可 https://code.visualstudio.com/ ​ 2.进入VS Code安装如下插件 二.C/C++开发 ...

  4. 如何从一台OPC Server访问多个PLC

    项目中遇到如下情况: 1. 整条生产线由多个PLC分别控制,但是所有PLC在同一个局域网内.PLC采用西门子的S7-200 Smart 2. 客户希望在操作工站的电脑(跟PLC在同一个局域网内)上提供 ...

  5. BUAA_2020_软件工程_软件案例分析作业

    项目 内容 这个作业属于那个课程 班级博客 这个作业的要求在哪里 作业要求 我在这个课程的目标是 学习掌握软件工程的相关知识 这个作业在哪个具体方面帮我实现目标 通过对具体软件案例的分析学习软件工程 ...

  6. 《基于SIR的路边违停行为传播模型研究》

    My Focus: 路边违停 行为的传播模型; 学习基于SIR XXX模型的可行性分析.建立和结论分析 Author: 左忠义,王英英,包蕴 Mind Map:

  7. 排序算法:Java实现希尔排序

    希尔排序的思路是先分组再整合 先对下标进行分组,比如当数组长度为20时,一开始选定一个间隔值为10 对数组进行排序,每隔10个元素比较大小并交换,以下标为间隔,1和11比较.2和12比较......1 ...

  8. Python reload(sys) NameError: name 'reload' is not defined

    转载:Python reload(sys) NameError: name 'reload' is not defined - vercont - 博客园 (cnblogs.com) 对于 Pytho ...

  9. python re:正向肯定预查(?=)和反向肯定预查(?<=)

    参考资料:https://tool.oschina.net/uploads/apidocs/jquery/regexp.html (?=pattern) 正向肯定预查,在任何匹配pattern的字符串 ...

  10. amba web

    arm amba doc https://developer.arm.com/docs