2022牛客寒假算法基础集训营1

赛场上完成度:11/12

rank:33

 

A

https://ac.nowcoder.com/acm/contest/23106/A

和的数根=数根的和,因此每个人的权值等价于权值的数根。

设f[i][j]表示前i个人凑出j的方案数,直接根据意义转移即可。

代码:

#include<bits/stdc++.h>
using namespace std;
const int mo=998244353;
int a[1000010],b[1000010],s[1000010];
int cal(int x)
{
	while (x>=10)
	{
		int y=x,z=0;
		while (y)
		{
			z=z+y%10;
			y=y/10;
		}
		x=z;
	}
	return x;
}
int main()
{
	int n;
	scanf("%d",&n);
	for (int i=1;i<=n;i++)
	{
		int x;
		scanf("%d",&x);
		a[i]=cal(x);
	}
	for (int i=1;i<=n;i++)
	{
		for (int j=1;j<=9;j++) b[j]=0;
		b[a[i]]++;
		for (int j=1;j<=9;j++) b[cal(a[i]+j)]=(b[cal(a[i]+j)]+s[j])%mo;
		for (int j=1;j<=9;j++) s[j]=(s[j]+b[j])%mo;
	}
	for (int i=1;i<=9;i++) printf("%d ",s[i]);
}

 

B

https://ac.nowcoder.com/acm/contest/23106/B

不难发现,对于模3相同的初始分数,在经过相同的区间之后,分数的变化量相同。

因此可以考虑分块,预处理每一块对模3为0,1,2的初始分数的影响,对于每次询问,块前后的部分暴力,对于整块直接计算对答案的影响。

总复杂度O(n+qn^0.5)

代码:

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10;
int le[500],ri[500],g[500][5],fr[N];
int main()
{
	int n,q;
	scanf("%d%d",&n,&q);
	char s[200010];
	scanf("%s",s+1);
	int k=(int)sqrt(n);
	//printf("%d\n",k);
	int cc=n/k;
	for (int i=1;i<=cc;i++)
	{
		le[i]=(i-1)*k+1;
		ri[i]=i*k;
	}
	if (n%k) 
	{
		cc++;
		le[cc]=(cc-1)*k+1;
		ri[cc]=n;
	}
	for (int i=1;i<=cc;i++)
	{
		for (int p=0;p<=2;p++)
		{
			int x=p;
			for (int j=le[i];j<=ri[i];j++)
			{
				if (s[j]=='W') x++;
				if (s[j]=='L'&&x%3) x--;
				fr[j]=i;
			}
			g[i][p]=x-p;
		}
	}
	for (int i=1;i<=q;i++)
	{
		int l,r,x;
		scanf("%d%d%d",&l,&r,&x);
		int cl=fr[l],cr=fr[r];
		if (cl==cr)
		{
			for (int j=l;j<=r;j++)
			{
				if (s[j]=='W') x++;
				if (s[j]=='L'&&x%3) x--;
			}
			printf("%d\n",x);
			continue;
		}
		for (int j=l;j<=ri[cl];j++)
		{
			if (s[j]=='W') x++;
			if (s[j]=='L'&&x%3) x--;	
		}
		for (int j=cl+1;j<cr;j++) x=x+g[j][x%3];
		for (int j=le[cr];j<=r;j++)
		{
			if (s[j]=='W') x++;
			if (s[j]=='L'&&x%3) x--;	
		}
		printf("%d\n",x);
	}
}

 

C

https://ac.nowcoder.com/acm/contest/23106/C

贪心地想,如果i和前面某个语句冲突,则在i前插入刚好能解决冲突的空指令。

这个贪心正确性显然。

#include<bits/stdc++.h>
using namespace std;
int st[100000],rk[100000];;
int main()
{
	int n,tl=0,ans=0;
	scanf("%d",&n);
	for (int i=1;i<=n;i++)
	{
		int p=-1;
		for (int j=1;j<4;j++)
		{
			int x;
			scanf("%d",&x);
			if (i-j<=0) continue;
			if (x==0) continue;
			if (p==-1) p=i-j;
		}
		if (p!=-1) 
        {
            while (tl-rk[p]<3) tl++,ans++;
        }
		st[++tl]=i;
		rk[i]=tl;
	}
	printf("%d\n",ans);
}

 

D

https://ac.nowcoder.com/acm/contest/23106/D

第一问,2*3*5*...一直到再乘超过n。

第二问,不大于n的最大质数。

证明略了,懒得打(之前打了一份,网站崩溃没保存,寄!

#include<bits/stdc++.h>
using namespace std;
int zs[100];
int pd(int x)
{
	for (int i=2;i*i<=x;i++) if (x%i==0) return 0;
	return 1;	
}
int main()
{
	int k=0;
	for (int i=1;i<=23;i++) if (pd(i)) zs[++k]=i;
	int t;
	scanf("%d",&t);
	while (t)
	{
		t--;
		int n;
		scanf("%d",&n);
		if (n==1)
		{
			printf("-1\n");
			continue; 
		}
		int x=1,j=1;
		while (x*zs[j]<=n&&j<=k)
		{
			x=x*zs[j];
			j++;
		}
		printf("%d ",x);
		for (int i=n;i>=2;i--)
		{
			if (pd(i)) 
			{
				printf("%d\n",i);
				break;	
			}
		}
		//if (n==2) printf("%d\n",n);
	}
}

 

E

https://ac.nowcoder.com/acm/contest/23106/E

纯纯的签到,特判一下边界。

每次可以运出m-1个人,最后一轮最多运m个人。

#include<bits/stdc++.h>
using namespace std;
int main()
{
	int t;
	scanf("%d",&t);
	while (t)
	{
		t--;
		int x,y;
		scanf("%d%d",&x,&y);
		if (y==1)
		{
			printf("-1\n");
			continue;
		}
		if (x<=y)
		{
			printf("1\n");
			continue;	
		}
		int ans=0;
		/*int z=x/(y-1);
		x=x%(y-1);
		ans=ans+(z-1)*2;
		ans=ans+1;
		if (y==2) ans=ans-2;
		printf("%d\n",ans);*/
		x=x-y;
		ans=ans+x/(y-1)*2;
		if (x%(y-1)) ans=ans+2;
		printf("%d\n",ans+1);
	}
}

 

F

https://ac.nowcoder.com/acm/contest/23106/F

大于等于m的数记为1,其他记为0,合法情况就是1的个数>0的个数,贪心地考虑切分,如果一段可以切成两份,则两份的1都要大于各种的0的数量,因此,扫一遍,能切就切。

#include<bits/stdc++.h>
using namespace std;
int a[1000010];
int main()
{
	int t;
	scanf("%d",&t);
	while (t)
	{
		t--;
		int n,m,s1=0;
		scanf("%d%d",&n,&m);
		for (int i=1;i<=n;i++) 
		{
			scanf("%d",&a[i]); 
			if (a[i]>=m) a[i]=1,s1++;else a[i]=0;
		}
		if (s1<n/2+1)
		{
			printf("-1\n");
			continue;
		}
		int q1=0,q2=0,ans=0;
		for (int i=1;i<=n;i++) 
		{
			if (a[i]) q1++,s1--;
			q2++;
			if (q1>=q2/2+1&&((n-i)/2+1<=s1)||(n-i)==0)
			{
				ans++;
				q1=0;
				q2=0;
			}
		}
		printf("%d\n",ans);
	}
}

G

https://ac.nowcoder.com/acm/contest/23106/G

不会。

 

H

https://ac.nowcoder.com/acm/contest/23106/H

树状数组优化一下暴力的过程即可。

#include<bits/stdc++.h>
using namespace std;
int a[1000010],b[1000010];
long long tr1[1000010],tr2[1000010];
void p1(int x,int y)
{
	while (x<=1001)
	{
		tr1[x]+=y;
		x=x+(x&(-x));
	}
}
void p2(int x,int y)
{
	while (x<=1001)
	{
		tr2[x]+=y;
		x=x+(x&(-x));
	}
}
long long q1(int x)
{
	long long ret=0;
	while (x)
	{
		ret+=tr1[x];
		x=x-(x&(-x));	
	}
	return ret;
}
long long q2(int x)
{
	long long ret=0;
	while (x)
	{
		ret+=tr2[x];
		x=x-(x&(-x));	
	}
	return ret;
}
int main()
{
	//freopen("test.in","r",stdin);
	int n;
	scanf("%d",&n);
	for (int i=1;i<=n;i++) 
	{
		scanf("%d",&a[i]);
		b[a[i]]++;
	}
	long long ans1=0;
	//for (int i=1;i<=n;i++) for (int j=i;j<=n;j++) ans1=ans1+abs(a[i]+a[j]-1000);
	long long ans=0,ss=0;
	for (int i=n;i>=1;i--)
	{
		p1(a[i]+1,1);
		p2(a[i]+1,a[i]);
		ss=ss+a[i];
		long long x=q1(1000-a[i]+1),y=q2(1000-a[i]+1);
		ans=ans+x*1000-x*a[i]-y-(n-i+1-x)*1000+(n-i+1-x)*a[i]+ss-y;
	}
	printf("%lld\n",ans);
	//printf("%d\n",ans1);
	//for (int i=1;i<=n;i++) printf("%d\n",a[i]);
	//printf("%lld\n",ans-ans1);
}

 

I

https://ac.nowcoder.com/acm/contest/23106/I

m句歌词和1句歌词的区别,就是答案乘m。

每个人的策略应当相同,考虑每个人的策略是一个概率p,即有p的概率唱,那么这句歌词无效的概率就是p^n+(1-p)^n,要使得这个概率最小,可得p=0.5

#include<bits/stdc++.h>
using namespace std;
const int mo=1e9+7;
int ksm(int x,int y)
{
	int ret=1;
	while (y)
	{
		if (y&1) ret=1ll*ret*x%mo;
		x=1ll*x*x%mo;
		y=y>>1;
	}
	return ret;
}
int main()
{
	int t;
	scanf("%d",&t);
	while (t)
	{
		t--;
		int n,m;
		scanf("%d%d",&n,&m);
		if (n==1) printf("0\n");
		else 
		{
			printf("%lld\n",1ll*m*(1-2*ksm(ksm(2,mo-2),n)%mo+mo)%mo);
		}
	}
}

 

J

https://ac.nowcoder.com/acm/contest/23106/J

每次考虑加入一个小朋友,要使得当前已经加入的小朋友的集合是合法的,贪心地考虑,每次选择权值最大的小朋友,如果小朋友是安静的,那加入后不会影响合法性,如果小朋友是闹腾的,就要判断加入后是否会由合法变为不合法。

最后看看集合大小是否能过达到n

#include<bits/stdc++.h>
using namespace std;
int a[100010],b[100010];
int main()
{
	int t;
	scanf("%d",&t);
	while (t)
	{
		t--;
		int n,m,s;
		scanf("%d%d%d",&n,&m,&s);
		for (int i=1;i<=n;i++) scanf("%d",&a[i]);
		for (int i=1;i<=m;i++) scanf("%d",&b[i]);
		sort(a+1,a+n+1);
		reverse(a+1,a+n+1);
		sort(b+1,b+m+1);
		reverse(b+1,b+m+1);
		int i=1,j=1,k=0;
		long long ans=0;
		while (1)
		{
			if (k==s) break;
			if (i<=n&&a[i]>=b[j])
			{
				k++;
				ans=ans+a[i];
				i++;
				continue;
			}
			if (j<=m&&b[j]>=a[i]&&j*2<=k+1)
			{
				k++;
				ans=ans+b[j];
				j++;
				continue;
			}
			if (i<=n)
			{
				k++;
				ans=ans+a[i];
				i++;
				continue;	
			}
			if (j*2<=k+1)
			{
				k++;
				ans=ans+b[j];
				j++;
				continue;
			}
			break;
		}
		if (s==k) printf("%lld\n",ans);else printf("-1\n");
	}
}

 

K

https://ac.nowcoder.com/acm/contest/23106/K

设f[l][i][j][k]表示,前l个岛屿,后三个岛屿依次是i,j,k,所能拥有的最多的绿岛数量。

转移,每次枚举下一个岛屿的颜色,判断新的组合是否满足S中罗盘的预测,再开一个G数组记录某种状态是否存在合法的方案,新状态只能从合法的旧状态转移过来。

#include<bits/stdc++.h>
using namespace std;
int g[100010][4][4][4],f[100010][4][4][4];
int main()
{
	int n;
	scanf("%d",&n);
	char s[100010];
	scanf("%s",s+1);
	for (int i=1;i<=3;i++) for (int j=1;j<=3;j++) for (int k=1;k<=3;k++)
	{
		int gs=0,rs=0,bs=0;
		if (i==1) gs++;
		if (j==1) gs++;
		if (k==1) gs++;
		if (i==2) rs++;
		if (j==2) rs++;
		if (k==2) rs++;
		if (i==3) bs++;
		if (j==3) bs++;
		if (k==3) bs++;
		g[3][i][j][k]=-1;
		if (s[3]=='G'&&gs>rs)
		{
			g[3][i][j][k]=1;
			f[3][i][j][k]=gs;	
		}
		if (s[3]=='R'&&rs>gs)
		{
			g[3][i][j][k]=1;
			f[3][i][j][k]=gs;	
		}
		if (s[3]=='B'&&rs==gs)
		{
			g[3][i][j][k]=1;
			f[3][i][j][k]=gs;
		}
	}
	for (int l=3;l<n;l++) 
	{
        for (int i=1;i<=3;i++) for (int j=1;j<=3;j++) for (int k=1;k<=3;k++) g[l+1][i][j][k]=-1;
		for (int i=1;i<=3;i++) for (int j=1;j<=3;j++) for (int k=1;k<=3;k++) if (g[l][i][j][k]!=-1)
		{
			for (int t=1;t<=3;t++)
			{	
				int gs=0,rs=0,bs=0;
				if (t==1) gs++;
				if (j==1) gs++;
				if (k==1) gs++;
				if (t==2) rs++;
				if (j==2) rs++;
				if (k==2) rs++;
				if (t==3) bs++;
				if (j==3) bs++;
				if (k==3) bs++;
				if (s[l+1]=='G'&&gs>rs)
				{
					g[l+1][j][k][t]=1;
					f[l+1][j][k][t]=max(f[l][i][j][k],f[l+1][j][k][t]);
				}
				if (s[l+1]=='R'&&gs<rs)
				{
					g[l+1][j][k][t]=1;
					f[l+1][j][k][t]=max(f[l][i][j][k],f[l+1][j][k][t]);
				}
				if (s[l+1]=='B'&&gs==rs)
				{
					g[l+1][j][k][t]=1;
					f[l+1][j][k][t]=max(f[l][i][j][k],f[l+1][j][k][t]);
				}
			}
		}
		for (int i=1;i<=3;i++) for (int j=1;j<=3;j++) if (g[l+1][i][j][1]!=-1) f[l+1][i][j][1]++;
	}
	int ans=-1;
	for (int i=1;i<=3;i++) for (int j=1;j<=3;j++) for (int k=1;k<=3;k++) if (g[n][i][j][k]!=-1) ans=max(ans,f[n][i][j][k]);
	printf("%d\n",ans);
}

  

 

L

https://ac.nowcoder.com/acm/contest/23106/L

模拟一下即可。

#include<bits/stdc++.h>
using namespace std;
int main()
{
	int t;
	scanf("%d",&t);
	while (t)
	{
		t--;
		int n;
		scanf("%d",&n);
		char s[100010];
		scanf("%s",s+1);
		int x=0,y=0;
		double ans=0;
		for (int i=1;i<=n;i++)
		{
			if (s[i]=='U') y++;
			if (s[i]=='D') y--;
			if (s[i]=='R') x++;
			if (s[i]=='L') x--;
			ans=max(ans,sqrt(x*x+y*y));
		}
		printf("%.8lf\n",ans);
	}
}

  

posted @ 2022-01-24 18:13  praying_cqf  阅读(89)  评论(0)    收藏  举报