NC224109. 零一奇迹
描述
小红拿到了一个长度为 的 01 串(仅由字符 '0' 和 '1' 组成的字符串)。
小红想知道,有多少子串满足,其所表示的二进制数,其对应的十进制数值在 和
之间(只统计不含前导零的子串)?
注 1 :两个子串哪怕长的一模一样,但只要取的位置不同,则定义为 2 个不同子串
注 2 :所谓子串(substring),指字符串删掉部分前缀和后缀(也可以不删)形成的字符串。
这个问题小红觉得可能太简单了,于是她决定强化该问题:
她可能会添加一些修改操作:每次选择一个位置进行翻转,即将 0 变成 1 或者 1 变成 0 。
输入描述
第一行三个正整数
、
和
。
第二行为一个长度为的 01 串。
第三行为一个正整数。代表修改的次数
接下来的
行,每行有一个正整数
,代表翻转第
位置上的字符。
输出描述
一共输出
行,每行一个整数,代表每次操作之后问题的答案。
请注意,每次操作后字符串的改动是会持续到未来的询问的。
示例1
输入:
5 2 4 10100 5 5 5 4 3 4
输出:
2 3 3 3 2
说明:
翻转 5 号位置以后, 字符串为10101,可能的结果有 10101 , 10101C++(g++ 7.5.0) 解法, 执行用时: 144ms, 内存消耗: 748K, 提交时间: 2023-05-01 21:37:07
#include<iostream> using namespace std; #define rep(i,l,r) for(int i = l; i <= r; i++) #define fep(i,a,b) for(int i=b; i>=a; --i) typedef long long ll; string s; ll n, l, r, q; ll cul(int L, int R) { ll ans = 0; rep(i,L,R) { if(s[i]=='0') continue; ll tmp = 0; for(int j=1;j<=64 and i+j-1<=n; j++) { int now = i + j - 1; tmp = (tmp<<1) + (s[now]-'0'); if(tmp >= l and tmp <= r) ans++; if(tmp > r) break; } } return ans; } void solve() { cin >> n >> l >> r >> s >> q; s=" "+s; ll ans=0; rep(i,1,q) { int x; cin >> x; ll t1 = cul(max(1, x-63), x); s[x] = (s[x] == '0') ? '1' : '0'; ll t2 = cul(max(1, x-63), x); if(i==1) ans = cul(1, n); else ans += t2 - t1; cout << ans << endl; } } int main() { ios::sync_with_stdio(false);cin.tie(0); solve(); return 0; }
C++ 解法, 执行用时: 85ms, 内存消耗: 520K, 提交时间: 2021-08-08 11:17:15
#include<bits/stdc++.h> using namespace std; typedef long long ll; #define Min(x,y)((x)<(y)?x:y) #define Max(x,y)((x)>(y)?x:y) #define For(i,x,y)for(i=x;i<=(y);i++) ll l,r; int n,k; char s[100005]; void calc(int i,int type) { int j; ll t=0; For(j,i,Min(i+65,n)) { t=t<<1|(s[j]-'0'); if(t>r)break; k+=(t>=l)*type; } } int main() { int q,i,a; scanf("%d%lld%lld%s%d",&n,&l,&r,s+1,&q); For(i,1,n) if(s[i]=='1')calc(i,1); while(q--) { scanf("%d",&a); For(i,Max(a-65,1),a) if(s[i]=='1')calc(i,-1); s[a]='1'-s[a]+'0'; For(i,Max(a-65,1),a) if(s[i]=='1')calc(i,1); printf("%d\n",k); } return 0; }