战况:(100+200+300+400=1000),时间 (40:39) ,(rk;1856),new rating(=202)。
感觉不错,但是仍然做不出 (E,F) 题。
下面是简要题解:
(A.)
题意概括:
问一个字符串能否通过在最后加一个字符构成另一个字符串。
暴力匹配即可:
char s1[15],s2[15];
int main()
{
scanf("%s",s1);
scanf("%s",s2);
for(int i=0;i<strlen(s1);i++)
{
if(s1[i]==s2[i]) continue;
else {printf("No
");return 0;}
}
printf("Yes
");
return 0;
}
(B.)
题意概括:
给 (A) 个 (1),(B) 个 (0),(C) 个 (-1),问选 (k) 个得到的最大值。
贪心分类讨论即可:
int a,b,c,k;
int main()
{
read(a),read(b),read(c),read(k);
if(k<=a) printf("%d
",k);
else if(k<=a+b) printf("%d
",a);
else printf("%d
",a-(k-a-b));
return 0;
}
(C.)
题意概括:
有 (n) 本书,每本书对于 (m) 个技能熟练度有加成,问达到加成所需的值最少花多少钱。
发现 (nleqslant 12),直接爆搜即可。
*,不好打,还不如直接状压,复杂度 (mathcal O(2^nnm))。
int n,m,x;
int c[20],a[20][20];
int ans,now,f;
int rt[20];
int main()
{
read(n),read(m),read(x);
ans=inf;
for(int i=1;i<=n;i++)
{
read(c[i]);
for(int j=1;j<=m;j++)
{
read(a[i][j]);
}
}
for(int i=0;i<=(1<<n)-1;i++)
{
memset(rt,0,sizeof(rt));
now=0;
f=0;
for(int j=0;j<=n-1;j++)
{
if(i&(1<<j))
{
now+=c[j+1];
for(int k=1;k<=m;k++) rt[k]+=a[j+1][k];
}
}
for(int j=1;j<=m;j++) if(rt[j]<x) f=1;
if(!f) ans=min(ans,now);
}
if(ans>2e9) printf("-1
");
else printf("%d
",ans);
return 0;
}
(D.)
题目描述:
每个地点有一个传送带,问传送 (k) 次到达的地点编号。
由于只有 (nleqslant 2*10^5),容易想到有循环节,长度最大为 (nleqslant 2*10^5)。
那么判断每个出现的元素,然后记录循环开始的地点和区间长度,最后分类即可,复杂度 (mathcal O(n))。
int n;
int a[200005];
ll k;
int rt[200005],cnt=0,p;
int b[200005],be,lo;
int main()
{
read(n),readl(k);
for(int i=1;i<=n;i++) read(a[i]);
rt[0]=1,p=1,b[1]=0;
while(1)
{
if(b[a[p]]!=0)
{
be=b[a[p]];
lo=cnt-be+1;
break;
}
rt[++cnt]=a[p];
b[a[p]]=cnt;
p=a[p];
}
if(k<=be) printf("%d
",rt[k]);
else
{
int s=(k-be)%lo;
printf("%d
",rt[s+be]);
}
return 0;
}
(E.)
题目简述:
给 (n) 个方格染 (m) 种颜色,要求最多有 (k) 组块相连且颜色相同,求方案数对 (998244353) 取模的结果。
组合数学,不会。
先上结论:
感性理解:
先确定第一个块的的颜色,有 (m) 种方法,然后有 (C_{n-1}^{n-1-i}=C_{n-1}^i) 种将剩下的那些染成不同色,然后对于剩下的 (n-i-1) 块,每种都有 (m-1) 种颜色来涂。
为什么组合数不用卢卡斯,因为模数太大了......
复杂度为 (mathcal O(klog n))
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
#define read(x) scanf("%d",&x)
#define MOD 998244353
int n,m,k;
int jc[200005];
int ans=0;
int quickpow(int a,int b)
{
int ans=1,base=a;
while(b)
{
if(b&1) ans=1ll*base*ans%MOD;
b>>=1;
base=1ll*base*base%MOD;
}
return ans%MOD;
}
void gjc(int n)
{
jc[0]=1;
for(int i=1;i<=n;i++) jc[i]=1ll*jc[i-1]*i%MOD;
return;
}
int inv(int a){return quickpow(a,MOD-2);}
int C(int n,int m){return 1ll*jc[n]*inv(1ll*jc[m]*jc[n-m]%MOD)%MOD;}
int main()
{
read(n),read(m),read(k);
gjc(n);
for(int i=0;i<=k;i++)
{
ans=(ans+1ll*m*C(n-1,i)%MOD*quickpow(m-1,n-i-1)%MOD)%MOD;
}
printf("%d
",ans%MOD);
return 0;
}
(F.)
题目简述:
给你 (n) 个括号串,问能否拼成一个合法的括号串。
我们发现只要合法,左括号能否匹配的右括号问题不大,要注意的是右括号的位置。
抽象一下,右括号消耗左括号。
同时,任何时候左括号的剩余数不能为负数。
那么我们考虑把左括号作为补充,右括号作为需求和削减,和这个题有很大的相似之处。
所以用栈模拟一下,把已经合法的子串去掉,然后排序即可。
至于原理,看那道题第一篇题解即可。
复杂度为 (mathcal O(sum len))。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
using namespace std;
int n;
char s[1000005];
struct node1
{
int a,b;
}x[1000005];
int cnt=0,c=0;
struct node2
{
int a,b;
}y[1000005];
int st[1000005],top=0;
int sum=0,flag=0;
bool cmp1(node1 n,node1 m){return n.a+n.b>m.a+m.b;}
bool cmp2(node2 n,node2 m){return n.a<m.a;}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
int now=0;
top=0;
scanf("%s",s);
int len=strlen(s);
for(int j=0;j<len;j++)
{
if(s[j]==')'&&!top) now++;
else if(s[j]=='(') top++;
else top--;
}
int s,t;
if(now!=0) s=now,t=top-now;
else s=0,t=top;
if(t<0) x[++cnt].a=s,x[cnt].b=t;
else y[++c].a=s,y[c].b=t;
}
sort(x+1,x+cnt+1,cmp1),sort(y+1,y+c+1,cmp2);
for(int i=1;i<=c;i++)
{
if(sum<y[i].a) flag=1;
sum+=y[i].b;
}
for(int i=1;i<=cnt;i++)
{
if(sum<x[i].a) flag=1;
sum+=x[i].b;
}
if(flag||sum) printf("No
");
else printf("Yes
");
return 0;
}
终于线下阿克了一场 (ABC)。