牛客小白月赛36
牛客小白月赛36
A 好哥哥
题意
给定一段合法括号序列和元钱,合法括号序列的定义如下:
1.是合法的括号序列。
2.若字符串是合法的括号序列,那么(A)也是合法的括号序列。
3.若字符串A,B是合法的括号序列,那么AB也是合法的括号序列。
我们设定表示第对括号的层数,即:它前面有多少未匹配的左括号。同时规定一对括号A是另一对括号B的好哥哥,当且仅当且括号A在括号B内。
如果当前位于一对括号Q,每次可以花费1元跳到:
1.它的任意一个好哥哥。
2.一对括号X,要求X的好哥哥是Q。
假如一开始位于第一对括号,请问最多可以经过多少对不重复的括号?
Solution
对括号序列建树,找到最长链,其余部分可以随便跑,代价是。根据能否跑完最长链计算答案。
代码
#include<bits/stdc++.h> using namespace std; int n,m; char s[20000005]; int main() { scanf("%d%d",&m,&n); scanf("%s",s); int maxn=0; int cnt=0; int l=0; for(int i=0;i<2*m;i++) { if(s[i]=='(') { cnt++; l++; } else l--; if(l==0)break; maxn=max(maxn,l); } int ans=0; if(n+1<maxn)ans=n+1; else ans=min(cnt,maxn+(n-maxn+1)/2); printf("%d",ans); return 0; }
B 最短串
题意
给定a,b两个串,要求找到最短的字符串s,其子串既包含a,也包含b,其中
Solution
通过数据范围5e3可知,这题可以暴力寻找以下四种情况(后两种情况选其一,即要么a是b的子串,要么b是a的子串)。
1、a的前缀和b的后缀相同,其相同的最大长度
2、a的后缀和b的前缀相同,其相同的最大长度
3、a是b的子串
4、b是a的子串
代码
#include <bits/stdc++.h> using namespace std; #define INF 0x3f3f3f3f #define ls i<<1 #define rs i<<1|1 typedef long long ll; typedef pair<int,int>P; char a[5005],b[5005]; int main() { scanf("%s%s",a,b); int n=strlen(a); int m=strlen(b); int maxn=0; for(int i=1;i<=min(n,m);i++) { bool flag=true; for(int j=0;j<i;j++) { if(a[j]=='?'||b[m-i+j]=='?')continue; if(a[j]!=b[m-i+j]) { flag=false; break; } } if(flag) { maxn=max(i,maxn); continue; } flag=true; for(int j=0;j<i;j++) { if(b[j]=='?'||a[n-i+j]=='?')continue; if(b[j]!=a[n-i+j]) { flag=false; break; } } if(flag)maxn=max(i,maxn); } if(n>m) { for(int i=0;i+m-1<n;i++) { bool flag=true; for(int j=0;j<m;j++) { if(a[i+j]=='?'||b[j]=='?')continue; if(a[i+j]!=b[j]) { flag=false; break; } } if(flag) { maxn=max(m,maxn); break; } } } else { for(int i=0;i+n-1<m;i++) { bool flag=true; for(int j=0;j<n;j++) { if(b[i+j]=='?'||a[j]=='?')continue; if(b[i+j]!=a[j]) { flag=false; break; } } if(flag) { maxn=max(n,maxn); break; } } } printf("%d",n+m-maxn); return 0; }
C 杨辉三角
题意
为杨辉三角中第n行提取出来的数,
求,答案对取模,其中
Solution
代码
#include <bits/stdc++.h> using namespace std; #define INF 0x3f3f3f3f #define ls i<<1 #define rs i<<1|1 typedef long long ll; typedef pair<int,int>P; const int mod=99824353; int fpow(ll x,ll ti) { ll ans=1; while(ti) { if(ti&1)ans=ans*x%mod; x=x*x%mod; ti>>=1; } return ans; } ll n; int main() { scanf("%lld",&n); n--; if(n==0)return 0*printf("0"); else if(n==1)return 0*printf("1"); ll res=(n%mod)*((n+1)%mod)%mod*fpow(2,n-2)%mod; printf("%lld",res); return 0; }
D 哥三好
题意
三个人分别有元钱,他们经常去网吧,去一次网吧,其中一人一次性买三个人的单,网吧的电脑分为三种价位100元,150元,250元每晚/人,每次都会定三台相同价位的电脑,现在问有多少种请客方案,请客顺序不同算不同方案,最终答案对1e9+7取模,其中
Solution
从数据范围来看,请一次客有300,450,750三种选择,因此可以先把都除以150,然后暴力背包就行了。最后枚举剩下的钱把多有答案累加起来。
代码
#include<bits/stdc++.h> using namespace std; const int mod=1e9+7; int a,b,c; int dp[300][300][300]; int main() { cin>>a>>b>>c; a/=150;b/=150;c/=150; dp[0][0][0]=1; for(int j=0;j<=a;j++) { for(int k=0;k<=b;k++) { for(int w=0;w<=c;w++) { if(dp[j][k][w]) { dp[j+2][k][w]=(dp[j+2][k][w]+dp[j][k][w])%mod; dp[j][k+2][w]=(dp[j][k+2][w]+dp[j][k][w])%mod; dp[j][k][w+2]=(dp[j][k][w+2]+dp[j][k][w])%mod; dp[j+3][k][w]=(dp[j+3][k][w]+dp[j][k][w])%mod; dp[j][k+3][w]=(dp[j][k+3][w]+dp[j][k][w])%mod; dp[j][k][w+3]=(dp[j][k][w+3]+dp[j][k][w])%mod; dp[j+5][k][w]=(dp[j+5][k][w]+dp[j][k][w])%mod; dp[j][k+5][w]=(dp[j][k+5][w]+dp[j][k][w])%mod; dp[j][k][w+5]=(dp[j][k][w+5]+dp[j][k][w])%mod; } } } } int res=0; for(int i=0;i<=1;i++) { for(int j=0;j<=1;j++) { for(int k=0;k<=1;k++) { if(a-i>=0&&b-j>=0&&c-k>=0) res=(res+dp[a-i][b-j][c-k])%mod; } } } printf("%d",res); return 0; }
E 皇城PK
题意
有名选手会进行次比赛,每次比赛不会出现平局的情况,只会有一个胜者。视胜者选手的实例比败者的实例强,如果出现选手A打败选手B,选手B打败选手C,选手C打败选手A,则视为他们的实例全部相同。
问按现在已有的比赛结果,有多少个选手可能获得冠军(如果两个人实力一样,则都不能获得冠军)。
其中
Solution
把败者标记一下,只要有一次失败,则一定不可能成为冠军,最后统计未标记的人有多少个。
代码
#include <bits/stdc++.h> using namespace std; #define INF 0x3f3f3f3f #define ls i<<1 #define rs i<<1|1 typedef long long ll; typedef pair<int,int>P; const int mod=1e9+7; int n,m; int vis[100005]; int main() { scanf("%d%d",&n,&m); for(int i=0;i<m;i++) { int a,b; scanf("%d%d",&a,&b); vis[b]=1; } int num=0; for(int i=1;i<=n;i++) { if(vis[i])continue; num++; } printf("%d",num); return 0; }
F 象棋
题意
给定的期盼,全部摆满炮,视所有炮都不属于同一阵营,他们之间可以相互攻击且不能不进行攻击直接移动。请问经历若干次攻击,直到不能攻击后,最少能剩余多少个炮。
Solution
首先要知道象棋中炮的攻击规则,炮只能攻击所在行或列里面隔着一个炮的炮
通过这个性质可以知道攻击完后最多剩下两行,最多剩下两列,因此答案就是
代码
#include <bits/stdc++.h> using namespace std; #define INF 0x3f3f3f3f #define ls i<<1 #define rs i<<1|1 typedef long long ll; typedef pair<int,int>P; const int mod=1e9+7; int t; ll n,m; int main() { scanf("%d",&t); while(t--) { scanf("%lld%lld",&n,&m); n=min(2ll,n); m=min(2ll,m); printf("%lld\n",n*m); } return 0; }
G 永不言弃
题意
有个关卡,对于每个关卡有两种通过方式。小沙的初始属性为,当角色的属性大于关卡的最高上限时,能强行通过该关卡,或者拥有通过该关卡的必过道具(并不是消耗品)。每个关卡通过后可能会开启一个或多个后置关卡,但每个关卡开启也需要若干个前置关卡,只有前置关卡全部通过后,才能开启该关卡。tips:除了一号关卡,如果一个关卡没有任何前置关卡,那么该关卡无法永远开启。
每个关卡只能玩一次,并且通过后能增强小沙角色的属性,也会给一个必过道具。目前小沙能否将这个游戏打通。
Solution
拓扑排序+优先队列
用两个队列模拟一下过程,从第一个关卡开始打,如果属性大于该后置关卡上限,或者有必过道具,那么就能通过该后置关卡,获得其道具,把这个关卡的编号放到另一个队列中,如果当前关卡还有没有通过的后置关卡,那么该关卡的编号也要放到另一个队列中,操作完这个队列后再对另一个队列进行相同的操作,当整个队列中没有关卡再能通过时(如果能通过则能进行下一次另一个队列的模拟),跳出循环。
代码
#include<bits/stdc++.h> using namespace std; typedef long long ll; int n; ll s; int vis[50005]; int pre[50005]; int a[50005]; int b[50005]; int c[50005]; int d[50005]; vector<int>e[50005]; int main() { scanf("%d%lld",&n,&s); for(int i=1;i<=n;i++)scanf("%d%d",&a[i],&b[i]); for(int i=1;i<=n;i++)scanf("%d%d",&c[i],&d[i]); for(int i=1;i<=n;i++) { int k;scanf("%d",&k); for(int j=0;j<k;j++) { int x;scanf("%d",&x); e[i].push_back(x); pre[x]++; } } queue<int>q[5]; int id=0; int res=0; if(s>=a[1]) { res++; q[0].push(1); vis[d[1]]=1; s+=c[1]; } while(true) { bool flag=true; while(!q[id].empty()) { int now=q[id].front();q[id].pop(); bool ff=true; for(int i=0;i<e[now].size();i++) { if(e[now][i]==-1)continue; int x=e[now][i]; //cout<<x<<endl; if(vis[b[x]]||s>=a[x]) { s+=c[x]; vis[d[x]]=1; e[now][i]=-1; pre[x]--; if(pre[x]==0) { q[id^1].push(x); res++; } flag=false; }else ff=false; } if(!ff)q[id^1].push(now); } if(flag)break; id^=1; } //cout<<res<<endl; if(res==n)printf("Yes"); else printf("No"); }
H 卷王之王
题意
有个数字,第个数字为,次操作,每次操作给出一个数字,将所有值小于等于的数字都加上,当次操作完成后,按顺序输出这个数字。
其中
Solution
据说正解可以用优先队列模拟,把的部分剪枝掉。
这边是通过线段树+剪枝通过的。
记录一下区间最小值,如果区间最小值大于,那么该区间就不用加上,直接返回就可以了。
再记录一下区间最大值,如果区间最大值小于等于,那么说明该区间的数都可以加上,加上后返回即可。
代码
#include <bits/stdc++.h> using namespace std; #define INF 0x3f3f3f3f #define ls i<<1 #define rs i<<1|1 typedef long long ll; typedef pair<int,int>P; int n,m; int a[100005]; ll res[100005]; struct node { int l,r; ll minn; ll maxn; ll lazy; ll x; }tree[100005*4]; void push_up(int i) { tree[i].minn=min(tree[ls].minn,tree[rs].minn); tree[i].maxn=max(tree[ls].maxn,tree[rs].maxn); } void build(int i,int l,int r) { tree[i].l=l; tree[i].r=r; tree[i].lazy=0; if(l==r) { tree[i].x=a[l]; tree[i].maxn=a[l]; tree[i].minn=a[l]; return; } int mid=(l+r)/2; build(ls,l,mid); build(rs,mid+1,r); push_up(i); } void push_down(int i) { if(tree[i].l==tree[i].r)return; tree[ls].lazy+=tree[i].lazy; tree[rs].lazy+=tree[i].lazy; tree[ls].maxn+=tree[i].lazy; tree[rs].maxn+=tree[i].lazy; tree[ls].minn+=tree[i].lazy; tree[rs].minn+=tree[i].lazy; tree[i].lazy=0; } void update(int i,ll val) { if(tree[i].minn>val)return; if(tree[i].maxn<=val) { tree[i].lazy+=val; tree[i].maxn+=val; tree[i].minn+=val; return ; } if(tree[i].l==tree[i].r)return; push_down(i); update(ls,val); update(rs,val); push_up(i); } void query(int i) { push_down(i); if(tree[i].l==tree[i].r) { int id=tree[i].l; res[id]=tree[i].maxn; return ; } query(ls); query(rs); push_up(i); } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++)scanf("%d",&a[i]); build(1,1,n); while(m--) { int x;scanf("%d",&x); update(1,x); } query(1); for(int i=1;i<=n;i++)printf("%lld ",res[i]); return 0; }
I 四面楚歌
题意
一个的区域,每块格子有以下几种类型:
.:表示为空地
1:表示为敌兵,不许通过,敌兵不会移动
0:表示为我方士兵
我方士兵只能上/下/左/右移动,问有多少个士兵能移出边界
Solution
要知道每个士兵能不能突围,正着思考需要知道每个我方士兵能否到达边界,这样无疑会tle,但是反着思考(0,0)点能到达多少个我方士兵,通过bfs就能找到多少士兵能够突围。
代码
#include <bits/stdc++.h> using namespace std; #define INF 0x3f3f3f3f #define ls i<<1 #define rs i<<1|1 typedef long long ll; typedef pair<int,int>P; const int mod=1e9+7; int n,m; char s[1005][1005]; int dx[]={1,-1,0,0},dy[]={0,0,1,-1}; queue<P>q; int d[1005][1005]; int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) scanf("%s",s[i]+1); memset(d,INF,sizeof(d)); d[0][0]=0; q.push(P(0,0)); int res=0; while(!q.empty()) { P p=q.front();q.pop(); for(int i=0;i<4;i++) { int x=dx[i]+p.first; int y=dy[i]+p.second; if(x<0||x>n+1||y<0||y>m+1)continue; if(d[x][y]!=INF||s[x][y]=='1')continue; d[x][y]=0; if(s[x][y]=='0') { //cout<< x<<' '<<y<<endl; s[x][y]='.'; res++; } q.push(P(x,y)); } } printf("%d",res); return 0; }
J 科学幻想
题意
长度为的字符串,有个指令,指令有以下两种:
1:将第个字符修改为
2:判断区间的字符串与区间的字符串是否勉强相等。
勉强相等指的是长度相等,对应位置上最多有一个位置的字符不同。
Solution
1.对于修改字符,可以线段树进行单点修改字符
2.对于字符串是否勉强相等,先判断两个区间长度是否相等,如果相等则二分对hash值进行判断:
左边部分相同,则可以向右缩小区间
左边部分不相同,右边部分相同,则可向左缩小区间
左边部分和右边部分都不相同,则说明这两个区间不勉强相等
代码
#include <bits/stdc++.h> #define ls i<<1 #define rs i<<1|1 using namespace std; typedef long long ll; const int P=131; const int mod=1e9+7; int n,m; char s[100005]; int p[100005]; struct node { int l,r; int x; }tree[400000+5]; void push_up(int i) { int len=tree[rs].r-tree[rs].l+1; tree[i].x=(1ll*tree[ls].x*p[len]%mod+tree[rs].x)%mod; } void build(int i,int l,int r) { tree[i].l=l; tree[i].r=r; if(tree[i].l==tree[i].r) { tree[i].x=s[l]-'a'; return ; } int mid=(l+r)/2; build(ls,l,mid); build(rs,mid+1,r); push_up(i); } void update(int i,int l,int val) { if(tree[i].l==l&&tree[i].r==l) { tree[i].x=val; return ; } int mid=(tree[i].l+tree[i].r)/2; if(mid>=l)update(ls,l,val); if(mid<l)update(rs,l,val); push_up(i); } node query(int i,int l,int r) { if(l>r)return tree[i]; if(l<=tree[i].l&&tree[i].r<=r) { return tree[i]; } int mid=(tree[i].l+tree[i].r)/2; if(l<=mid&&r>mid) { node a=query(ls,l,r); node b=query(rs,l,r); a.r=b.r; a.x=(1ll*a.x*p[b.r-b.l+1]%mod+b.x)%mod; return a; } else if(l<=mid)return query(ls,l,r); else if(r>mid)return query(rs,l,r); } bool solve(int l1,int r1,int l2,int r2) { if((r2-l2)!=(r1-l1))return false; int l=0,r=r2-l2; while(l<=r) { int mid=(l+r)/2; int lsum1=query(1,l1,l1+mid).x; int lsum2=query(1,l2,l2+mid).x; if(lsum1==lsum2) { l=mid+1; continue; } int rsum1=query(1,l1+mid+1,r1).x; int rsum2=query(1,l2+mid+1,r2).x; if(rsum1==rsum2) { r=mid-1; continue; } return false; } return true; } int main() { ios::sync_with_stdio(0); cin.tie(0); cout.tie(0); cin>>n>>m; cin>>(s+1); p[0]=1; for(int i=1;i<=n;i++)p[i]=1ll*p[i-1]*P%mod; //scanf("%d%d",&n,&m); //scanf("%s",s+1); build(1,1,n); while(m--) { int op;//scanf("%d",&op); cin>>op; if(op==1) { int x;char ch[5]; cin>>x>>ch; //scanf("%d%s",&x,ch); update(1,x,ch[0]-'a'); } else { int l1,l2,r1,r2; cin>>l1>>r1>>l2>>r2; //scanf("%d%d%d%d",&l1,&r1,&l2,&r2); if(solve(l1,r1,l2,r2))cout<<"YES\n";//printf("YES\n"); else cout<<"NO\n";//printf("NO\n"); } } return 0; }