ABC 167 A~F 题解

战况:(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) 取模的结果。

组合数学,不会。

先上结论:

[ans=sumlimits_{i=0}^k mC_{n-1}^i(m-1)^{n-1-i} ]

感性理解:
先确定第一个块的的颜色,有 (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)

原文地址:https://www.cnblogs.com/tlx-blog/p/12867954.html