牛客周赛 Round 72
怎么全是01串
A
枚举除了末尾的字符,判断下一个是否与它不同,不同则对答案的贡献++
B
找一个连续子串是好串,如果我们找到长度为len的子串,那么从中任意截取一段均为好串
长度为len的子串 1个
长度为len-1的子串 2个
.....
长度为2的子串 len-1个
用等差数列公式
一个长度为len的好串子串的个数为
C
构造一个解的情况还是很容易的,但是一些情况需要特判
以下情况都不能构成
- 此时有0有1则不可能构造
- 此时的a个0和b个1,形成的01串最多有min(a,b)*2-1对不相邻的01,因为,长度为a+b的串最多有a+b-1对01,而此时a==b
- 假设此时b>a ,那么可以拿a个0和b个1构成上述的a*2-1对字符串,而此时1有多余的,还可以加入到字串末尾多形成一对01
可以构造的情况就简单了
先生成足够01,然后将多余的0,1放在两边
#include<bits/stdc++.h>
using namespace std;
int t;
int a,b,k;
int main(){
cin>>t;
while(t--){
cin>>a>>b>>k;
if(k==0){
if(a && b){
cout<<-1<<endl;
}
else {
while(a){--a;cout<<"0";}
while(b){--b,cout<<"1";}
cout<<endl;
}
}
else {
if(a==b && k>min(a,b)*2-1) cout<<-1<<endl;
else if(a!=b && k>min(a,b)*2){
cout<<"-1"<<endl;
}
else {
if(k&1){
int mid=k+1;
for(int i=1;i<=a-mid/2;++i) cout<<0;
for(int i=1;i<=mid/2;++i) cout<<"01";
for(int i=1;i<=b-mid/2;++i) cout<<1;
}
else {
int mid=k;
if(a>=b){
for(int i=1;i<=a-mid/2-1;++i) cout<<0;
for(int i=1;i<=mid/2;++i) cout<<"01";//只有k-1对01
for(int i=1;i<=b-mid/2;++i) cout<<1;
cout<<0; //补上一个
}
else{
cout<<1;//前面补上
for(int i=1;i<=a-mid/2;++i) cout<<0;
for(int i=1;i<=mid/2;++i) cout<<"01";//只有k-1对
for(int i=1;i<=b-mid/2-1;++i) cout<<1;
}
}cout<<endl;
}
}
}return 0;
}
D
一眼DP,但是状态转移时顺序错误了
应该从后往前转移
由题可知,只能转移最近相同字符和不同字符,
如果按照顺序来转移,举例00110,转移到第二个1时,最近的0是第一个1前一个,但因为0只能转移到最近的1,所以不能从这个0转移
这里的错误在于,忽略了01之间存在其他的有效转移,导致转移出错
如果按照逆序来转移,同样的例子,转移第一个1前面的0时,只能第一个1转移,末尾的0转移,这样逆转过来的顺序便不会出错
再深究一点,因为每一个位置,只有唯一的向后转移的顺序,所以反过来找时唯一的
#include<bits/stdc++.h>
using namespace std;
int n;
const int maxn=1e5+10;
int x,y;
int l1=-1,l0=-1;
string s;
long long f[maxn];
int main(){
cin>>n>>x>>y;
cin>>s;
for(int i=0;i<=n;++i) f[i]=1e15+10;
f[n-1]=0;
if(s[n-1]=='0') l0=0;
else l1=0;
for(int i=n-1;i>=0;--i){
if(s[i]=='0'){
if(l0!=-1)f[i]=min(f[i],f[l0]+x);
if(l1!=-1)f[i]=min(f[i],f[l1]+y) ;
l0=i;
}
else{
if(l0!=-1)f[i]=min(f[i],f[l0]+y);
if(l1!=-1)f[i]=min(f[i],f[l1]+x);
l1=i;
}
}cout<<f[0]<<endl;
return 0;
}
E
首先,不要误解题目,01串代表的正整数,不是二进制数
根据同余的性质,同乘性和同加性,一个数从高位乘到低位,余数不变
定义
表示前i个位置,取模k的值为j的方案总数
转移方程
if(s[i+1]=='0')//是0,乘10,取余
dp[i+1][j*10%13]+=dp[i][j];
else if(s[i+1]=='1')//是1,乘10+1,取余
dp[i+1][(j*10+1)%13]+=dp[i][j];
else {//是?两种情况都考虑
dp[i+1][(j*10+1)%13]+=dp[i][j];
dp[i+1][j*10%13]+=dp[i][j];
}
有人可能有疑问,只是有?的地方才需要状态转移把,如果?前面都是01,那么构成的整数同余的方案数不会变啊,只需要用最后两个状态转移啊
但是如果一旦经过了?,那么上面的转移方程就都需要有用了
#include<bits/stdc++.h>
using namespace std;
int n;
#define ll long long
const int maxn=2e5+10,mod=1e9+7;
string s;
ll dp[maxn][16];
int main(){
cin>>s;
dp[0][0]=1;//初始化
n=s.length();s=" "+s;
for(int i=0;i<n;++i)
for(int j=0;j<=12;++j){
if(s[i+1]=='0')
dp[i+1][j*10%13]+=dp[i][j];
else if(s[i+1]=='1')
dp[i+1][(j*10+1)%13]+=dp[i][j];
else {
dp[i+1][(j*10+1)%13]+=dp[i][j];
dp[i+1][j*10%13]+=dp[i][j];
}
dp[i+1][j*10%13]%=mod;
dp[i+1][(j*10+1)%13]%=mod;
}
cout<<dp[n][0];//是13的倍数,取余后为0
}
F
我来讲讲补完这个题的收获吧
- 线段树离散化
将查询区间端点排序,重新构成小区间,将每个小区间作为叶节点,这样查询的时候一定能查到由小区间构成的区间
建树时的区间应该为端点个数来建立
- 明确父节点和子节点的关系
只有将父节点,子节点的关系明确,才能进行push_down,push_up,以及设置懒标记的操作
- 确定节点的信息
节点的信息对push_down,push_up,lazy等相当重要,而且往往节点信息是我们所求的答案
- push_down
每次的push_down要清除,当前节点的懒标记,并让子节点继承,而且子节点的信息会根据懒标记更新,而且如果子节点也有懒标记,那么就要思考怎么处理父节点懒标记和子节点懒标记的继承问题
#include<bits/stdc++.h>
#define ls (p<<1)
#define rs (p<<1|1)
using namespace std;
const int maxn=3e5+10;
int t;vector<int>v;
int m,n,q;
int getidx(int x){
return lower_bound(v.begin(),v.end(),x)-v.begin()+1;
}
struct qy{
int op,l,r;
}a[maxn];
struct node{
int l,r;
int ranl,ranr;
int num0,num1;
int laze_tag;
int lazy_1;
}tr[maxn<<2];
void cal(int p,int lt,int l1){
int len=tr[p].ranr-tr[p].ranl+1;
//先1后翻,全部变零
if(lt && l1 && lt>l1){
tr[p].num0=len/2;
tr[p].num1=(len+1)/2;
}
//翻后 1操作
else if(l1){
//此时全为1
tr[p].num0=(len+1)/2;
tr[p].num1=len/2;
}
//全部翻转,变好串次数交换
else if(lt){
swap(tr[p].num0,tr[p].num1);
}
}
void push_down(int p){
if(tr[p].lazy_1) tr[ls].lazy_1=tr[rs].lazy_1=tr[p].lazy_1;
//down下去取反操作
if(tr[p].laze_tag){//根据父节点,确定子节点,懒标记
if(tr[ls].lazy_1<tr[ls].laze_tag)
tr[ls].laze_tag=0;
else tr[ls].laze_tag=tr[p].laze_tag;
if(tr[rs].lazy_1<tr[rs].laze_tag)
tr[rs].laze_tag=0;
else tr[rs].laze_tag=tr[p].laze_tag;
}
//更改num0,num1
cal(ls,tr[p].laze_tag,tr[p].lazy_1);
cal(rs,tr[p].laze_tag,tr[p].lazy_1);
tr[p].laze_tag=tr[p].lazy_1=0;
}
void push_up(int p){
tr[p].ranl=tr[ls].ranl;
tr[p].ranr=tr[rs].ranr;
tr[p].num0=tr[ls].num0;
int lenl=tr[ls].ranr-tr[ls].ranl+1;
////左边区间长度是奇数且0开头,那么末尾是0,
//就可以和右边的1开头区间合并
if(lenl & 1) tr[p].num0+=tr[rs].num1;
//偶区间,结尾是1,右边0开头的区间合并
else tr[p].num0+=tr[rs].num0;
//与上面同理
tr[p].num1=tr[ls].num1;
if(lenl & 1) tr[p].num1+=tr[rs].num0;
else tr[p].num1+=tr[rs].num1;
}
void build(int p,int l,int r){
tr[p].l=l;tr[p].r=r;
if(l==r){
//区间离散化
tr[p].ranl=v[l-1];
tr[p].ranr=v[l]-1;
int len=tr[p].ranr-tr[p].ranl+1;
//一开始全为0
tr[p].num0=len/2;//修改区间成 以0开头的好串的最小修改次数
tr[p].num1=(len+1)/2;//修改区间成 以1开头的好串的最小修改次数
return ;
}
int mid=(l+r)>>1;
build(ls,l,mid);
build(rs,mid+1,r);
push_up(p);
}
void update(int p,int nl,int nr,int op,int num){
int l=tr[p].l,r=tr[p].r;
if(nl<=l && r<=nr){
if(op==1){
tr[p].lazy_1=num;//记录最近的1操作是第几次
cal(p,0,num);
}
if(op==2){
if(tr[p].lazy_1<tr[p].laze_tag) tr[p].laze_tag=0;
else tr[p].laze_tag=num;
cal(p,num,0);
}
return;
}
push_down(p);
int mid=(l+r)>>1;
if(nl<=mid) update(ls,nl,nr,op,num);
if(nr>mid) update(rs,nl,nr,op,num);
push_up(p);
}
node query(int p,int nl,int nr){
int l=tr[p].l,r=tr[p].r;
if(nl<=l && r<=nr) return tr[p];
push_down(p);
int mid=(l+r)>>1;
node ans;
ans.num0=-1;
if (nl <= mid) {
node tmp=query(ls, nl, nr);
ans=tmp;
}
if(nr>mid) {
node tep=query(rs,nl,nr);
if(ans.num0==-1) ans=tep;
else {
int len1=ans.ranr-ans.ranl+1;
if(len1 & 1) ans.num0+=tep.num1;
else ans.num0+=tep.num0;
if(len1 & 1) ans.num1+=tep.num0;
else ans.num1+=tep.num1;
ans.ranr=tep.ranr;
}
}return ans;
}
int main(){
cin.tie(0);cout.tie(0);
ios::sync_with_stdio(0);
cin>>m>>q;
for(int i=1;i<=q;++i){
cin>>a[i].op>>a[i].l>>a[i].r;
v.push_back(a[i].l);
v.push_back(a[i].r+1);
}
sort(v.begin(),v.end());
v.erase(unique(v.begin(),v.end()),v.end());
int n=v.size()-1;
build(1,1,n);
for(int i=1;i<=q;++i){
int op=a[i].op;
int l=getidx(a[i].l),r=getidx(a[i].r+1)-1;
if(op==1) update(1,l,r,1,i);
else if(op==2) update(1,l,r,2,i);
else{
node ans=query(1,l,r);
cout<<min(ans.num0,ans.num1)<<endl;
}
}
return 0;
}
本文作者:归游
本文链接:https://www.cnblogs.com/guiyou/p/18620857
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步