A Registration
给两个字符串,长度为 \(n\) 和 \(n+1\)。检查第二个字符串的前 \(n\) 位是不是第一个字符串。枚举一下每一位即可。
#include<bits/stdc++.h>
using namespace std;
string s,t;
int main(){
cin>>s>>t;
bool ans=1;
for(int i=0;i<s.size();i++) if(s[i]!=t[i]) ans=0;
cout<<(ans?"Yes":"No");
return 0;
}
B Easy Linear Programming
对于选出的 \(k\) 张卡片,肯定优先选择 \(1\) 的,再选 \(0\) 的,最后是 \(-1\) 的。于是我们分类讨论即可,
#include<bits/stdc++.h>
using namespace std;
int n,m,x,y,z,ans;
int main(){
cin>>x>>y>>z>>n;
if(x>=n) ans=n;
else if(x+y>=n) ans=x;
else ans=x-(n-x-y);
cout<<ans;
return 0;
}
C Skill Up
暴力枚举每一本书选不选,然后判断是否合法最后更新答案即可,
#include<bits/stdc++.h>
using namespace std;
const int N=109;
int n,m,x,c[N],a[N][N],p[N],ans=0x3f3f3f3f;
void dfs(int s,int price){
if(s==n+1){
for(int i=1;i<=m;i++) if(p[i]<x) return;
ans=min(ans,price);
return;
}
dfs(s+1,price);
for(int i=1;i<=m;i++) p[i]+=a[s][i];
dfs(s+1,price+c[s]);
for(int i=1;i<=m;i++) p[i]-=a[s][i];
}
int main(){
cin>>n>>m>>x;
for(int i=1;i<=n;i++){
cin>>c[i];
for(int j=1;j<=m;j++) cin>>a[i][j];
}
dfs(1,0);
if(ans>1e9) cout<<-1;
else cout<<ans;
return 0;
}
D Teleporter
用dfs处理出环,然后分类讨论,国王有没有走到环上。由于走到换上就不会出来了,直接取模即可。
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=2e5+9;
int n,k; bool vst[N],in[N]; int tot,sl,a[N],cnt,loop[N];
void dfs(int u){
tot++; vst[u]=1; int v=a[u];
if(vst[v]) sl=v;
else dfs(v);
}
signed main(){
scanf("%lld%lld",&n,&k);
for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
dfs(1); int u=sl; int find=0;
while(1){
if(u==sl&&find) break;
find=1, loop[++cnt]=u, in[u]=1;
u=a[u];
} loop[0]=loop[cnt];
if(k<=tot-cnt){
int ans=1;
for(int i=1;i<=k;i++) ans=a[ans];
cout<<ans;
}else{
k++;
cout<<loop[(k-(tot-cnt))%cnt];
}
return 0;
E Colorful Blocks
看到题立马想到一个 \(O(nk)\) 的 \(dp\)…… 可惜这题是个组合题。
没关系,有 \(dp\) 可以找规律观察。\(f(i,j)\) 表示前 \(i\) 个格子有 \(j\) 对相等的。
然后经过一系列的推导(我不会)和列式子观察(这个才是),我们发现每一个 \(f(i,j)\) 都是一个可以化作乘积的形式,常数项是 \(C_{i-1}^{j}\),且都含有 \(m\) 和 \(m-1\),其中 \(m\) 的指数是 \(1\),\(m-1\) 的指数是一个连续递减数列,为 \(i-j-1\)。于是我们可以搞得式子:
这个故事告诉我们:手动打表和多项式很重要
最后求出 \(\sum f(n,j)\) 即可。
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e6+9,mod=998244353;
int n,m,k,ans,fact[N],inv[N];
int qp(int a,int b=mod-2){
return (b==0?1:(b%2?qp(a*a%mod,b/2)*a%mod:qp(a*a%mod,b/2)));
}
int C(int a,int b){return fact[a]*inv[b]%mod*inv[a-b]%mod;}
signed main(){
scanf("%lld%lld%lld",&n,&m,&k);
for(int i=0;i<=n;i++) fact[i]=(i==0?1:fact[i-1]*i%mod);
for(int i=n;i>=0;i--) inv[i]=(i==n?qp(fact[n]):inv[i+1]*(i+1)%mod);
for(int j=0;j<=k;j++){
ans=(ans+m*C(n-1,j)%mod*qp(m-1,n-j-1))%mod;
}
printf("%lld",ans);
return 0;
}
F Bracket Sequencing
这题其实需要一个构造。按照想好的构造方案去弄,如果可行,那么就是Yes,否则就是No。
这题和这题有很大的相似之处。我们可以把左括号理解成回血,右括号理解为掉血。
这个字符串的前缀和(左括号为+1,右括号为-1)的最小值即可以理解为最大的耗血,最大值可以理解为最大的回血。
对于一个字符串,如果它的总和是非负的,我们肯定先打最小值最大的(即最小耗血)。
对于一个字符串,如果它的总和是负的,按照比赛讨论区的题解的说法,他们是对称的;按照上面那题的题解来说,我们可以反过来看,即右括号变成+1,左括号-1,那么显然应该从右往左选前缀最大值最小的,即从左往右看,后缀最大值最大的。
所以最后再统计一下即可。
int n,ac,bc;
struct str{int m;string st;}a[N],b[N];
bool cmp(const str&a,const str&b){return a.m>b.m;}
string last="";
int main(){
cin>>n;
for(int i=1;i<=n;i++){
string s; cin>>s;
int sum=0,mx=-1e9,mn=1e9;
for(int j=0;j<s.size();j++){
if(s[j]=='(') sum++;
else sum--;
mn=min(mn,sum);
} sum=0;
for(int j=s.size()-1;j>=0;j--){
if(s[j]=='(') sum++;
else sum--;
mx=max(mx,sum);
}
if(sum>=0) a[++ac]=(str){mn,s};
else b[++bc]=(str){mx,s};
}
sort(a+1,a+ac+1,cmp), sort(b+1,b+bc+1,cmp);
for(int i=1;i<=ac;i++) last+=a[i].st;
for(int i=1;i<=bc;i++) last+=b[i].st;
int sum=0;
for(int i=0;i<last.size();i++){
if(last[i]=='(') sum++;
else sum--;
if(sum<0) return puts("No"),0;
}
if(sum==0) puts("Yes");
else puts("No");
return 0;
}