Codeforces Round #804 (Div. 2) A-E题解

视频讲解:BV1vN4y1M7cx

A. The Third Three Number Problem

题目大意

给定整数 n ( 1 ≤ n ≤ 1 0 9 ) n(1\leq n \leq 10^9) n(1n109) ,求任意一组 a , b , c ( 0 ≤ a , b , c ≤ 1 0 9 ) a,b,c(0 \leq a,b,c \leq 10^9) a,b,c(0a,b,c109) ,满足
( a ⊕ b ) + ( b ⊕ c ) + ( a ⊕ c ) = n (a \oplus b)+(b \oplus c)+(a \oplus c)=n (ab)+(bc)+(ac)=n

无解输出 − 1 -1 1

题解

仅考虑最低位的情况,会发现:

  • 若三个数都为 00 00 00 01 01 01 ,则总和为 00 00 00
  • 若一个数为 01 01 01 ,另两个数为 00 00 00 ,则总和为 10 10 10
  • 若一个数为 00 00 00 ,另两个数为 01 01 01 ,则总和为 10 10 10

因此对于任意一位,要不总和为 0 0 0 ,要不向更高位进一位。

因此若 n n n 为奇数,则无解。
有解时,一种合法的方案为 ( n 2 , 0 , 0 ) (\frac{n}{2},0,0) (2n,0,0)

参考代码

#include
using namespace std;
typedef long long ll;

int main()
{
	int T,n;
	scanf("%d",&T);
	while(T--)
	{
		scanf("%d",&n);
		if(n&1)
			printf("-1\n");
		else
			printf("%d %d %d\n",n/2,0,0);
	}
}

B. Almost Ternary Matrix

题目大意

给定两个偶数 n , m ( 2 ≤ n , m ≤ 50 ) n,m(2\leq n,m \leq 50) n,m(2n,m50) ,求 n × m n \times m n×m 的01矩阵,满足对于矩阵内任意位置 ( i , j ) (i,j) (i,j) ,在四邻域上均有恰好2个与该元素不同的元素。

题解

用以下矩阵及其旋转90°的矩阵填充即可:

01
10

参考代码

#include
using namespace std;
typedef long long ll;

const int MAXN=55;
int a[MAXN][MAXN];

int main()
{
	int T,n,m,i,j;
	scanf("%d",&T);
	while(T--)
	{
		scanf("%d%d",&n,&m);
		memset(a,0,sizeof(a));
		for(i=0;i<n;i+=2)
		{
			for(j=0;j<m;j+=2)
			{
				if((i/2+j/2)%2)
					a[i][j]=a[i+1][j+1]=1;
				else
					a[i+1][j]=a[i][j+1]=1;
			}
		}
		for(i=0;i<n;i++)
		{
			for(j=0;j<m;j++)
				printf("%d ",a[i][j]);
			puts("");
		}
	}
}

C. The Third Problem

题目大意

给定长度为 n n n 1 1 1 n − 1 n-1 n1 的排列 a a a ,求有多少排列 b b b 与排列 a a a “相似”。
“相似”的定义为,对于任意区间 [ l , r ] ( 1 ≤ l ≤ r ≤ n ) [l,r](1 \leq l \leq r \leq n) [l,r](1lrn) ,均有
M E X ( [ a l , a l + 1 , . . . , a r ] ) = M E X ( [ b l , b l + 1 , . . . , b r ] ) MEX([a_l,a_{l+1},...,a_r])=MEX([b_l,b_{l+1},...,b_r]) MEX([al,al+1,...,ar])=MEX([bl,bl+1,...,br])

题解

根据 M E X MEX MEX 的定义,我们需要从小到大逐个考虑每个数。
p o s i pos_i posi 表示排列 a a a 中元素 i i i 出现的位置。

考虑用类似数学归纳法的思想,求解排列 b b b 的方案数。

初始区间 [ p o s 0 , p o s 0 ] [pos_0,pos_0] [pos0,pos0] M E X MEX MEX 值必须为 1 1 1 ,因此 0 0 0 位置确定,方案数为 1 1 1

设当前区间为 [ l , r ] [l,r] [l,r] 0 0 0 i − 1 i-1 i1 的元素均在改区间中。
对于元素 i i i

  • p o s i ∈ [ l , r ] pos_i\in [l,r] posi[l,r] ,则在排列 b b b 中元素 i i i 也必须在 [ l , r ] [l,r] [l,r] 中,可选位置数为 r − l + 1 − i r-l+1-i rl+1i ,因此方案数 ∗ ( r − l + 1 − i ) *(r-l+1-i) (rl+1i)
  • p o s i ∉ [ l , r ] pos_i \notin [l,r] posi/[l,r] ,则在排列 b b b 中元素 i i i 必须在 p o s i pos_i posi 位置上,方案数不变,并且 [ l , r ] [l,r] [l,r] 扩展至 [ m i n ( l , p o s i ) , m a x ( r , p o s i ) ] [min(l,pos_i),max(r,pos_i)] [min(l,posi),max(r,posi)]

参考代码

#include
using namespace std;
typedef long long ll;

const ll mod=1e9+7;
const int MAXN=100100;
int pos[MAXN];

int main()
{
	int T,n,x,i,l,r;
	ll ans;
	scanf("%d",&T);
	while(T--)
	{
		scanf("%d",&n);
		for(i=1;i<=n;i++)
		{
			scanf("%d",&x);
			pos[x]=i;
		}
		l=r=pos[0];
		ans=1;
		for(i=1;i<n;i++)
		{
			if(l<=pos[i]&&pos[i]<=r)
				ans=ans*(r-l+1-i)%mod;
			else
			{
				l=min(l,pos[i]);
				r=max(r,pos[i]);
			}
		}
		printf("%lld\n",ans);
	}
}

D. Almost Triple Deletions

题目大意

给定长度为 n ( 1 ≤ n ≤ 5000 ) n(1 \leq n \leq 5000) n(1n5000) 的数组 a a a 。进行若干次操作,每次操作可以选择一个 i ( 1 ≤ i < n ) i(1\leq i i(1i<n) ,满足 a i ≠ a i + 1 a_i \neq a_{i+1} ai=ai+1 ,然后删去 a i a_i ai a i + 1 a_{i+1} ai+1

以任意方式进行若干次操作,求最后剩下完全相同的元素时,数组最长可以是多少。

题解

考虑对于任意区间 [ l , r ] [l,r] [l,r] ,什么情况下能将其中的元素恰好完全删除。
满足以下两个条件时,方可:

  • r − l + 1 r-l+1 rl+1 为偶数
  • 区间的众数出现次数,不超过 n 2 \frac{n}{2} 2n

通过预处理或枚举区间端点等方式,可以很快判断任意区间能否完全删除。

d p i dp_i dpi 表示在前 i i i 个元素中,最后剩下 a i a_i ai 时,最多可以剩下多时少元素。若无法剩下 a i a_i ai 则为负无穷大。初始 d p 0 = 0 dp_0=0 dp0=0
则有转移式:
d p i = max ⁡ j = 0 i − 1 ( D e l e t a b l e ( j + 1 , r ) & ( a i = a j ∣ j = 0 ) ) ∗ ( d p j + 1 ) dp_i=\max_{j=0}^{i-1}(Deletable(j+1,r)\&(a_i=a_j|j=0))*(dp_j+1) dpi=j=0maxi1(Deletable(j+1,r)&(ai=ajj=0))(dpj+1)

两重循环枚举 i , j i,j i,j 即可。
枚举 j j j 时倒序枚举以便求解 D e l e t a b l e ( j + 1 , r ) Deletable(j+1,r) Deletable(j+1,r)

最终答案为
a n s = max ⁡ i = 1 n D e l e t a b l e ( i + 1 , n ) ∗ d p i ans=\max_{i=1}^{n} Deletable(i+1,n)*dp_i ans=i=1maxnDeletable(i+1,n)dpi

参考代码

#include
using namespace std;
typedef long long ll;

const int MAXN=5050;
const int inf=1<<30;
int a[MAXN],sum[MAXN],dp[MAXN];

int main()
{
	int T,n,i,j,mxnum,ans;
	scanf("%d",&T);
	while(T--)
	{
		scanf("%d",&n);
		for(i=1;i<=n;i++)
			scanf("%d",&a[i]);
		for(i=1;i<=n;i++)
		{
			memset(sum,0,sizeof(int)*(n+5));
			dp[i]=-inf;
			mxnum=0;
			for(j=i-1;j>=0;j--)
			{
				if((i-j-1)%2==0&&mxnum<=(i-j-1)/2&&(j==0||a[i]==a[j]))
					dp[i]=max(dp[i],dp[j]+1);
				sum[a[j]]++;
				mxnum=max(mxnum,sum[a[j]]);
			}
		}
		memset(sum,0,sizeof(int)*(n+5));
		ans=0;
		mxnum=0;
		for(i=n;i>=1;i--)
		{
			if((n-i)%2==0&&mxnum<=(n-i)/2)
				ans=max(ans,dp[i]);
			sum[a[i]]++;
			mxnum=max(mxnum,sum[a[i]]);
		}
		printf("%d\n",ans);
	}
}

E. Three Days Grace

题目大意

给定大小为 n ( 1 ≤ n ≤ 1 0 6 ) n(1 \leq n \leq 10^6) n(1n106) 的多重集合 A A A ,每个元素在 [ 1 , m ] ( 1 ≤ m ≤ 5 ⋅ 1 0 6 ) [1,m](1 \leq m \leq 5 \cdot 10^6) [1,m](1m5106) 范围内。

可以进行任意次操作。每次操作选择 A A A 中一个元素 x x x ,将其拆分为 x = p ⋅ q x=p\cdot q x=pq ,其中 p , q > 1 p,q >1 p,q>1 ,然后将 p , q p,q p,q 插入 A A A 中,并删除 x x x

max ⁡ ( a i ) − min ⁡ ( a i ) \max(a_i)-\min(a_i) max(ai)min(ai) 的最小值。

题解

参考了huangxiaohua的AC代码和官方题解。

考虑枚举 L = m i n ( a i ) L=min(a_i) L=min(ai) ,求解最小的 R = m a x ( a i ) R=max(a_i) R=max(ai)

从大到小枚举 L L L ,当枚举到新的 L L L 时,会发现对于 L L L 的倍数 j j j ,可能有更好的拆分方法,从而得到更小的 R R R 。对于不是 L L L 的倍数的数,并不会受到影响。

d p i , j dp_{i,j} dpi,j 表示将 j j j 拆分为若干因子后,最小因子为 i i i 时所有因子的最大值的最小值,其中 $j\geq i $ 且 i ∣ j i|j ij

对于 k ≥ i k\geq i ki , 若 d p k , j i ≥ i dp_{k,\frac{j}{i}}\geq i dpk,iji ,则可以将 j j j 拆出一个 i i i ,变成子状态 d p k , j i dp_{k,\frac{j}{i}} dpk,ij ,即

d p i , j = min ⁡ ( d p i , j , d p k , j i ) dp_{i,j}=\min(dp_{i,j},dp_{k,\frac{j}{i}}) dpi,j=min(dpi,j,dpk,ij)

实现时, i i i 维度可以通过从大到小枚举被压缩掉,复杂度 O ( m log ⁡ m ) O(m\log m) O(mlogm)

当枚举到新的 L L L 时,对于初始就有的元素 j j j ,其中 L ∣ j L|j Lj ,先删去所有旧的 d p k , j dp_{k,j} dpk,j ,其中 k > L k>L k>L ,再更新并添加上新的 d p L , j dp_{L,j} dpL,j

参考代码

#include
using namespace std;
typedef long long ll;

const int MAXM=5000500;
int has[MAXM],num[MAXM],dp[MAXM];

int main()
{
	int T,n,m,l,r,ans,i,j,x;
	scanf("%d",&T);
	while(T--)
	{
		scanf("%d%d",&n,&m);
		memset(has,0,sizeof(int)*(m+5));
		memset(num,0,sizeof(int)*(m+5));
		for(i=1;i<=m;i++)
			dp[i]=i;
		l=MAXM;r=-1;
		while(n--)
		{
			scanf("%d",&x);
			has[x]=num[x]=1;
			l=min(l,x);
			r=max(r,x);
		}
		ans=r-l;
		for(i=sqrt(m)+1;i>=1;i--)
		{
			for(j=i;j<=m;j+=i)
			{
				if(has[j])
					num[dp[j]]--;
				if(dp[j/i]>=i)
					dp[j]=min(dp[j/i],dp[j]);
				if(has[j])
					num[dp[j]]++;
			}
			while(num[r]==0)
				r--;
			ans=min(ans,r-min(i,l));
		}
		printf("%d\n",ans);
	}
}

你可能感兴趣的:(----Codeforces,动态规划,算法)