NC19469 01串
题目
题目描述
I used to believe
We were burning on the edge of something beautiful
Something beautiful
Selling a dream
Smoke and mirrors keep us waiting on a miracle
On a miracle
Say go through the darkest of days
Heaven's a heartbreak away
Never let you go
Never let me down
OH it's been a hell of a ride
Driving the edge of a knife
Never let you go
Never let me down
Bieber拥有一个长度为n的01 串,他每次会选出这个串的一个子串作为曲谱唱歌,考虑该子串从左往右读所组成的二进制数P。 Bieber每一秒歌唱可以让P增加或减少 2 的 k次方(k由Bieber选定),但必须保证任意时刻其P大于等于0。
Bieber 是一位追求效率的人 每次Bieber都想知道他歌唱的最少时间将这个数P变成0。
Bieber 正和 一位DJ合作,他随时可能修改串上的一个字符。
输入描述
第一行一个数n
第二行一个长度为n的字符串s
第三行一个数 t 表示 询问 + 修改总次数
以下 t 行, 每行格式如下
第一个数 1 <= type <= 2 表示 类型
Type = 1 表示是一次 询问 接下来两个数 l , r 表示询问的区间。
否则 表示一次修改 接下来两个数x,y 表示把 s[x] 改为y.
n <= 3e5, t <= 3e5
输出描述
对于每个询问输出一个数表示最少次数。
示例1
输入
4
1101
1
1 1 4
输出
3
题解
知识点:动态dp,区间dp,线段树。
先考虑不带修改操作只有询问,那就是一道简单的区间dp题,复杂度是 \(O(n^3) \sim O(1)\) 。当然如果只询问 \([1,n]\) ,那就是线性dp了,复杂度 \(O(n)\)。
设 \(f_{l,r,i,j}\) 代表考虑区间 \([l,r]\) ,左端点状态为 \(i(0/1)\) (是否向高位进位),右端点状态为 \(j(0/1)\) (是否从低位进位)时的操作次数最小值。
显然,两个区间合并成一个区间时,分割点位置是无后效性的。因为根据现有的状态,一个子区间状态的答案以及方案和包含它的区间的状态没有任何关系,可以推得无论分割点在哪,答案是固定的。
因此,状态转移方程为:
f_{l,r,i,j} = \min(f_{l,mid,i,0} + f_{mid+1,r,0,j},f_{l,mid,i,1} + f_{mid+1,r,1,j})
\end{aligned}
\]
其中 \(mid\) 从 \([l,r]\) 任选一点即可。
但是现在要求能够修改,那就必须使用线段树维护区间的dp信息了,也就是动态dp,每个线段树区间 \([l,r]\) 维护一个矩阵 \(f[0/1][0/1]\) 即可。维护信息的过程是十分显然的,因为此区间dp和分割点无关,就直接按照线段树的修改合并查询就做完了。
另外,区间信息的单位元值不好找,可以直接合并时特判,或者合并前排除无效区间,都可以的。
时间复杂度 \(O((n+m)\log n)\)
空间复杂度 \(O(n)\)
代码
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
struct T {
array<array<int, 2>, 2> f = { (int)1e9,(int)1e9,(int)1e9,(int)1e9 };
friend T operator+(const T &a, const T &b) {
// 这里用特殊值判断无效区间
// 当然,也可以在递归的时候直接避免掉无效区间,而不是在合并时才判断
if (a.f[0][0] == 1e9) return b;
if (b.f[0][0] == 1e9) return a;
auto x = T();
//* 广义矩阵乘法(乘改加,加改取最大值)
for (auto i : { 0,1 })
for (auto j : { 0,1 })
for (auto k : { 0,1 })
x.f[i][j] = min(x.f[i][j], a.f[i][k] + b.f[k][j]);
return x;
}
};
struct F {
bool upd;
T operator()(const T &x) {
return{
upd,1,
1,!upd
};
}
};
template<class T, class F>
class SegmentTree {
int n;
vector<T> node;
void update(int rt, int l, int r, int x, F f) {
if (r < x || x < l) return;
if (l == r) return node[rt] = f(node[rt]), void();
int mid = l + r >> 1;
update(rt << 1, l, mid, x, f);
update(rt << 1 | 1, mid + 1, r, x, f);
node[rt] = node[rt << 1] + node[rt << 1 | 1];
}
T query(int rt, int l, int r, int x, int y) {
if (r < x || y < l) return T();
if (x <= l && r <= y) return node[rt];
int mid = l + r >> 1;
return query(rt << 1, l, mid, x, y) + query(rt << 1 | 1, mid + 1, r, x, y);
}
public:
SegmentTree(int _n = 0) { init(_n); }
SegmentTree(const vector<T> &src) { init(src); }
void init(int _n) {
n = _n;
node.assign(n << 2, T());
}
void init(const vector<T> &src) {
assert(src.size() >= 2);
init(src.size() - 1);
function<void(int, int, int)> build = [&](int rt, int l, int r) {
if (l == r) return node[rt] = src[l], void();
int mid = l + r >> 1;
build(rt << 1, l, mid);
build(rt << 1 | 1, mid + 1, r);
node[rt] = node[rt << 1] + node[rt << 1 | 1];
};
build(1, 1, n);
}
void update(int x, F f) { update(1, 1, n, x, f); }
T query(int x, int y) { return query(1, 1, n, x, y); }
};
int main() {
std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int n;
cin >> n;
vector<T> a(n + 1);
for (int i = 1;i <= n;i++) {
char x;
cin >> x;
a[i] = {
x == '1',1,
1,x != '1'
};
}
SegmentTree<T, F> sgt(a);
int m;
cin >> m;
while (m--) {
int op;
cin >> op;
if (op == 1) {
int l, r;
cin >> l >> r;
cout << sgt.query(l, r).f[0][0] << '\n';
}
else {
int x;
bool val;
cin >> x >> val;
sgt.update(x, { val });
}
}
return 0;
}
NC19469 01串的更多相关文章
- JZOJ P1847:找01串
传送门 DP预处理+贪心 首先设$f[i][j]$表示长度为$i$的01串中有不大于$j$个1,然后显然 $f[i][j]=\sum_{k=1} ^{j} C[i][k]$ $C[i][j]=C[i- ...
- 洛谷P2727 01串 Stringsobits
P2727 01串 Stringsobits 24通过 55提交 题目提供者该用户不存在 标签USACO 难度普及+/提高 提交 讨论 题解 最新讨论 这题的思路是啥啊!!!跪求- 题目背景 考虑 ...
- C++实现01串排序
题目内容:将01串首先按长度排序,长度相同时,按1的个数从少到多进行排序,1的个数相同时再按ASCII码值排序. 输入描述:输入数据中含有一些01串,01串的长度不大于256个字符. 输出描述:重新排 ...
- 01串(dp)
01串 时间限制:1000 ms | 内存限制:65535 KB 难度:2 描述 ACM的zyc在研究01串,他知道某一01串的长度,但他想知道不含有“11”子串的这种长度的01串共有多少个, ...
- 【巧妙】【3-21个人赛】Problem C 01串
Problem C Time Limit : 3000/1000ms (Java/Other) Memory Limit : 65535/32768K (Java/Other) Total Sub ...
- NYOJ-252 01串
01串 时间限制:1000 ms | 内存限制:65535 KB 难度:2 描写叙述 ACM的zyc在研究01串,他知道某一01串的长度,但他想知道不含有"11"子串的这样的长 ...
- NYOJ 252 01串(斐波那契数列变形)
01串 时间限制:1000 ms | 内存限制:65535 KB 难度:2 描述 ACM的zyc在研究01串,他知道某一01串的长度,但他想知道不含有“11”子串的这种长度的01串共有多少个, ...
- COGS 862. 二进制数01串【dp+经典二分+字符串】
862. 二进制数01串 ★ 输入文件:kimbits.in 输出文件:kimbits.out 简单对比 时间限制:1 s 内存限制:128 MB USACO/kimbits(译 by ...
- 1415: 小ho的01串 [字符串]
点击打开链接 1415: 小ho的01串 [字符串] 题目描述 有一个由0和1组成的字符串,它好长呀--------一望无际 恩,说正题,小ho的数学不太好,虽然是学计算机的但是看见0和1也是很头疼的 ...
- bitset(01串)优化
bitset的经典使用: 见代码及注释: #include<bitset> #include<algorithm> using namespace std; //只需调用< ...
随机推荐
- Halcon代码导出到.net FrameWork/WPF
1. 应用背景 在工业项目中,往往需要使用机器视觉结合人机界面开发特定的,面向工艺的项目.机器视觉中,Halcon无疑是功能强大的,能快速应用到项目的视觉产品,而WPF则是解决人机界面的利器.因此了解 ...
- vue3学习大全(1)
# vue3.0 Vue3.0 在北京时间2020年9月19 日凌晨,发布了 3.0 版本,代号:*One Piece* ## 1.新特性 Vue 3 中一些需要关注的新功能包括: - [组合式 AP ...
- mysqld_safe Directory '/tmp/mysql' for UNIX socket file don't exists.
报错版本:mysql-5.7.35 1.报错完整提示信息: [root@localhost bin]# 2022-11-15T04:04:43.122905Z mysqld_safe Logging ...
- mysql 排序ROW_NUMBER() RANK() DENSE_RANK()
with 月业绩 as (SELECT 年份,月份, ROUND(sum(总业绩)/100000) 业绩 FROM `myj` group by 年份,月份) select * ,ROW_NUMBER ...
- 微信网页授权——获取code、access_token、openid,及跨域问题解决
首先在微信开发文档中有提到微信网页授权的操作步骤: 第一步:用户同意授权,获取code 在确保微信公众账号拥有授权作用域(scope参数)的权限的前提下(服务号获得高级接口后,默认拥有scope参数中 ...
- (Winform程序带源码) 弹出输入框和获取输入框的值
弹出输入框和获取输入框的值: private void button1_Click(object sender, EventArgs e) { string returnValue = Microso ...
- VUE配置proxy代理、开发环境、测试环境、生产环境
VUE配置proxy代理.开发环境.测试环境.生产环境 前端开发过程中,我们经常会碰到跨域的问题,下面我们来配置下,不同的环境下,统一的跨域问题解决. 1.根目录下新建三个环境的配置文件,.env.d ...
- iText5实现Java生成PDF
参考代码:https://github.com/youcong1996/study_simple_demo 参考博客:https://blog.csdn.net/weixin_37848710/art ...
- P8112 符文破译
题目描述 将字符串 \(T\) 拆成若干个子串,使这些子串为字符串 \(S\) 的前缀,要求拆分形成的子串数最小. 思路整理 实际上并不需要倒着枚举,也不需要线段树,更不需要 Z 函数. 如果你做过 ...
- 自己动手从零写桌面操作系统GrapeOS系列教程——20.汇编语言读硬盘实战
学习操作系统原理最好的方法是自己写一个简单的操作系统. 本讲我们设计一个简单的读硬盘实验.通过一定的方法使硬盘第二个扇区的前3个字节依次为1.2.3,最后3个字节依次为3.2.1,中间的506个字节全 ...