Summer Holiday Contest 2020 Day 1官方题解

D1T1

Description

给定一个01串,现在要取出一些数且他们两两不相邻,请求出最大的和。

Solution

Subtask 1

01 01 枚举每个位置是否被选即可,每次判断是否存在相邻的位置,如果不存在则用和来更新答案。

时间复杂度: O ( 2 n n ) O(2^nn)

Subtask 2

留给使用 O ( n 2 ) O(n^2) 算法的选手。

Subtask 3

做法1

考虑 d p dp

状态设计 d p i dp_i 表示从 1 1 i i 取不相邻的数所能得到的最大和。

显然,状态转移方程为 d p i = m a x ( d p i 2 + a i , d p i 1 ) dp_i=max(dp_{i-2}+a_i,dp_{i-1}) 。其中, d p i 2 + a i dp_{i-2}+a_i 表示,该位置的数要取且状态从前两个位置( d p i 2 dp_{i-2} )转移而来;而 d p i 1 dp_{i-1} 表示,该位置的数不选且状态从上一个位置转移而来。

最终答案就是 d p n dp_n

做法2

考虑,取 0 0 对答案并没有贡献,所以我们不取 0 0

对于每一段连续的 1 1 ,假设它的长度为 l e n len ,那么顶多能够取 u p ( l e n / 2 ) up(len/2) 个数。其中 u p ( l e n / 2 ) up(len/2) 表示 l e n / 2 len/2 向上取整。

于是,我们直接线性枚举全为 1 1 的区间,那么答案就是 Σ u p ( l e n / 2 ) Σ up(len/2)

综上所述,这两种做法的时间复杂度均为 O ( n ) O(n) 。注意后一种做法的常数更小。

Code

做法1:

#include <bits/stdc++.h>
#define int long long
using namespace std;

int n,ans=-1;
int a[1000005],dp[1000005];

signed main()
{
	cin>>n;
	for (int i=1;i<=n;i++)
	{
		char x;
		cin>>x;
		a[i]=x-'0';
	}
	dp[1]=a[1];
	
	for (int i=2;i<=n;i++)  dp[i]=max(dp[i-2]+a[i],dp[i-1]);
	for (int i=1;i<=n;i++)  ans=max(ans,dp[i]);
	cout<<ans<<endl;
	
	return 0;
}//by ducati

做法2:

#include<bits/stdc++.h>
using namespace std;
int main()
{
	int n,i,ans=0,tot=0;
	string s;
	cin>>n>>s;
	for(i=0;i<n;i++)
		if(s[i]=='0')
		{
			ans+=(tot+1)>>1;
			tot=0;
		}
		else
			tot++;
	cout<<ans+((tot+1)>>1);
	return 0;
}//by b6e0

D1T2

Description

请构造出 n n 个数,使得它们与 n 1 n-1 个运算符相连后得到的答案为 m m

Solution

Task 1

可以发现,一定存在答案使得每个数均不大于 m m 且运算后答案为 m m

所以,我们枚举每个位置的值,然后暴力计算即可;如果符合,就直接打印输出。

时间复杂度 O ( m n 2 ) O(mn^2)

Task 2-10

正解的做法显然,先在第一个位置填上 m m ,后面的所有数保持运算结果不变即可。

详细地说,我们这样:

①在第一个位置填上 m m
②接着填下去,如果
(1)前一个运算符为 + + ,则填0;
(2)前一个运算符为 × × ,则填1;
(3)前一个运算符为 - ,则填0;
(4)前一个运算符为 | ,则填0;
(5)前一个运算符为^,则填0;
(6)前一个运算符为&,注意此时每个二进制位都是1才能行,所以应该填 2 31 1 = 2147483647 2^{31}-1=2147483647

注意可以用 s w i t c h switch 语句。

时间复杂度: O ( n ) O(n)

Code

#include<bits/stdc++.h>
using namespace std;
int main()
{
	int n,m,i;
	char c;
	scanf("%d%d",&n,&m);
	printf("%d ",m);
	for(i=1;i<n;i++)
	{
		cin>>c;
		switch(c)
		{
			case '+':
				printf("0 ");
				break;
			case '-':
				printf("0 ");
				break;
			case '*':
				printf("1 ");
				break;
			case '&':
				printf("2147483647 ");
				break;
			case '|':
				printf("0 ");
				break;
			case '^':
				printf("0 ");
				break;
		}
	}
	return 0;
}//by b6e0

D1T3

Description

请求出存在多少个长度为 n n 的字符串,使得每位均为A或B或C或D,并且经过有限次的交换后无法得到连续的 A B C ABC

Solution

Subtask 1

首先,容易发现,如果暴力交换+暴力判断,本Subtask无法通过。

于是,我们发现,如果字符串中同时存在A,B,C,那么一定能够通过有限次交换得到连续的 A B C ABC

所以,我们直接暴力枚举每个位置上的字母,然后判断是否同时存在 A A , B B , C C 即可。如果不同时存在,那么答案加上1。

时间复杂度: O ( 4 n n ) O(4^nn)

Subtask 2

考虑 d p dp

状态设计 d p i , s 1 , s 2 , s 3 , s 4 dp_{{i},{s1},{s2},{s3},{s4}} i i 表示目前看到了第 i i 位, s 1 s1 目前是否存在 a a s 2 s2 表示目前是否存在 b b s 3 s3 表示目前是否存在 c c s 4 s4 表示目前是否存在 d d

显然,如果在该位置填上 A A ,那么应该从上一个位置中,所有 s 2 = 0 s2=0 s 3 = 0 s3=0 的状态转移而来,如果该位置填上 B B ,那么应该从上一个位置中,所有 s 1 = 0 s1=0 s 3 = 0 s3=0 的状态转移而来,填 C C 同理。注意填 D D 的话要继承所有使得 s 1 = 0 s1=0 s 2 = 0 s2=0 s 3 = 0 s3=0 的状态。

显然,最终答案就是,所有看到第 n n 位,且 s 1 = 0 s1=0 s 2 = 0 s2=0 s 3 = 0 s3=0 的状态之和。

注意取模。时间复杂度 O ( n ) O(n)

虽然常数很大,但是能够通过。

Subtask 3

通过基本的容斥原理,发现不同时含 A A , B B , C C 的串数量为:

{A,B,D}+{A,C,D}+{B,C,D}-{A,D}-{B,D}-{C,D}+{D}。

其中,{A,B,D}表示所有位为 A A B B D D 的字符串的数量,{D}表示所有位均为 D D 的字符串的数量 。

综上所述,答案就是

3 n + 3 n + 3 n 2 n 2 n 2 n + 1 n 3^n+3^n+3^n-2^n-2^n-2^n+1^n
= 3 n + 1 3 × 2 n + 1 =3^{n+1}-3×2^n+1

注意用快速幂进行优化。

时间复杂度 O ( l o g 2 n ) O(log_2n)

#include <bits/stdc++.h>
#define int long long
using namespace std;

int n,mod;

int quick_power(int a,int b)
{
	if (b==0)  return 1;
	
	int res=1;
	for (;b;b=b>>1,a=(a*a))
	{
		if (b&1)  res=(res*a);
	}
	return res;
}

signed main()
{
	cin>>n>>mod;
	cout<<((quick_power(3,n+1)-3*quick_power(2,n)+1)%mod+mod)%mod<<endl;
	
	return 0;
}//by ducati

D1T4

Description

给定一个迷宫,每个位置都指向另一个位置,求出从该位置走 k k 步后的位置。

Subtask 1

容易发现,从该位置出发后会形成长度为1的周期。

即,如果该位置指向自己,那么经过 k k 步后他仍然在这里;否则,他会在第一步到另一个位置,并且在那里不动。

Subtask 2

直接模拟即可。

时间复杂度: O ( n m + k ) O(nm+k)

Subtask 3-4

做法1

这是一道很经典的周期问题。

我们可以直接记录下上一次是什么时候到这个位置,如果现在又到达了这个位置,那么就能轻松得到周期的开始时间与周期的长度。

然后运用周期,将 k k 变小,再查表得出答案即可。

时间复杂度: O ( n 2 m 2 ) O(n^2 m^2) 。注意这是上限,事实上运行的时间(如果不故意卡的话)会远小于这个。

做法2

考虑倍增优化 d p dp

状态设计 d p i , j , k , s dp_{i,j,k,s} ,表示从位置 i , j i,j 2 k 2^k 步的位置——若 s = 0 s=0 则记录下走 2 k 2^k 步后的横坐标,否则( s = 1 s=1 )记录下走 2 k 2^k 步后的纵坐标。

状态转移显然,由于这是倍增优化 d p dp ,就是 d p i , j , k = d p d p i , j , k 1 , 0 , d p i , j , k 1 , 1 , k 1 dp_{i,j,k}=dp_{dp_{i,j,k-1,0},dp_{i,j,k-1,1},k-1}

但是, k k 不一定为 2 2 的次方数。我们运用"跳"的思想,每次跳 2 2 的次方数步。例如,当 k = 10 k=10 时,我们先跳 8 8 步,再跳 2 2 步。

于是就能很快地得到答案啦。时间复杂度 O ( n m l o g k ) O(nmlog_k)

Code

做法1

#include <bits/stdc++.h>
#define int long long
using namespace std;

int n,m,si,sj,k,l,r,step=0;
int visited[105][105];

struct node
{
	int ii;
	int jj;
}a[105][105];

struct node_ans
{
	int ai;
	int aj;
}ans[1000005];

signed main()
{
	cin>>n>>m>>si>>sj>>k;
	for (int i=1;i<=n;i++)
	{
		for (int j=1;j<=m;j++)
		{
			cin>>a[i][j].ii>>a[i][j].jj;
			visited[i][j]=-1;
		}
	}
	visited[si][sj]=0;
	ans[0].ai=si,ans[0].aj=sj;
	while (1)
	{
		int posi=a[si][sj].ii,posj=a[si][sj].jj;
		si=posi,sj=posj;
		step++;
		
		if (visited[si][sj]!=-1)
		{
			l=visited[si][sj];
			r=step-l;
			ans[step].ai=si,ans[step].aj=sj;
			break;
		}
		else
		{
			visited[si][sj]=step;
			ans[step].ai=si,ans[step].aj=sj;
		}
	}
	if (k<=l)  cout<<ans[k].ai<<' '<<ans[k].aj<<endl;
	else
	{
		k=(k-l)%r+l;
		cout<<ans[k].ai<<' '<<ans[k].aj<<endl;
	}
	return 0;
}//by ducati

做法2

#include<bits/stdc++.h>
using namespace std;
int po[105][105][2],dp[105][105][70][2];
int main()
{
	int n,m,a,b,i,j,k,x,y;
	long long t,s=1;
	cin>>n>>m>>a>>b>>t;
	for(i=1;i<=n;i++)
		for(j=1;j<=m;j++)
		{
			cin>>po[i][j][0]>>po[i][j][1];
			dp[i][j][0][0]=po[i][j][0];
			dp[i][j][0][1]=po[i][j][1];
		}
	for(k=1;k<=62;k++)
		for(i=1;i<=n;i++)
			for(j=1;j<=m;j++)
			{
				dp[i][j][k][0]=dp[dp[i][j][k-1][0]][dp[i][j][k-1][1]][k-1][0];
				dp[i][j][k][1]=dp[dp[i][j][k-1][0]][dp[i][j][k-1][1]][k-1][1];
			}
	for(i=0;i<62;i++)
		s*=2;
	for(;s;s/=2,i--)
		if(s<=t)
		{
			x=a;
			y=b;
			a=dp[x][y][i][0];
			b=dp[x][y][i][1];
			t-=s;
		}
	cout<<a<<' '<<b;
	return 0;
}//by b6e0

撒花✿✿ヽ(°▽°)ノ✿撒花

猜你喜欢

转载自blog.csdn.net/Cherrt/article/details/106582208
今日推荐