Codeforces Round #690 (Div. 3) CF1462A~F 题解

A.
根据题目给的构造方式逆推出对应的下标。
( n + 1 ) / 2 (n+1)/2 (n+1)/2位为奇数位,后面的为偶数位,前后顺序相反。

代码:

#include <iostream>
#include <cstdio>

const int maxn=307;

using namespace std;

int n,T;
int a[maxn],b[maxn];

int main()
{
	scanf("%d",&T);
	while (T--)
	{
		scanf("%d",&n);
		for (int i=1;i<=n;i++) scanf("%d",&a[i]);
		int k=(n+1)/2;
		for (int i=1;i<=k;i++) b[i*2-1]=a[i];
		for (int i=k+1;i<=n;i++) b[(n-i+1)*2]=a[i];
		for (int i=1;i<=n;i++) printf("%d ",b[i]);
		printf("\n");
	}
}

B.
显然需要存在一个前缀与一个后缀连起来等于字符 “ 2020 ” “2020” 2020即可。

代码:

#include <iostream>
#include <cstdio>

const int maxn=207;

using namespace std;

int n,T,flag;
char s[maxn],p[maxn];

int main()
{
	scanf("%d",&T);
	while (T--)
	{
		flag=0;
		scanf("%d",&n);
		scanf("%s",s+1);
		
		for (int i=0;i<=4;i++)
		{
			for (int k=1;k<=i;k++) p[k]=s[k];
			for (int k=1;k<=4-i;k++) p[i+k]=s[n-(4-i)+k];
			if ((p[1]=='2') && (p[2]=='0') && (p[3]=='2') && (p[4]=='0'))
			{
				printf("YES\n");
				flag=1;
				break;
			}
			if (flag) break;
		}
		if (!flag) printf("NO\n");
	}
}

C.
显然位数越小越优,位数相等首位最小最优。所以我们从9到1反着取数,如果加上某个 i i i后大于等于 x x x,那么把这个 i i i删去,换成 x − s u m i x-sumi xsumi即可,然后从小到大输出。

代码:

#include <iostream>
#include <cstdio>
#include <cmath>

using namespace std;

int n,T,p;

int main()
{
	scanf("%d",&T);
	while (T--)
	{
		scanf("%d",&n);
		int i;
		p=0;
		for (i=9;i>0;i--)
		{
			p+=i;
			if (p>=n) break;
		}
		if (p<n) printf("-1\n");
		else
		{
			p-=i;
			printf("%d",n-p);
			for (int j=i+1;j<=9;j++) printf("%d",j);
			printf("\n");
		}
	}
}

D.
因为只能合并相邻的数,所以问题相当于把数列分成若干段,每段的数的和相等。
我们可以枚举最后每段的值是多少,设 s u m = ∑ i = 1 n a i sum=\sum_{i=1}^{n}a_i sum=i=1nai,枚举 s u m sum sum的因数 x x x,然后判断是否可以使得每一段和都为 x x x即可。

代码:

#include <iostream>
#include <cstdio>
#include <cmath>

const int maxn=3007;

using namespace std;

int n,T,sum,ans;
int a[maxn];

bool check(int lim)
{
	int p=0;
	for (int i=1;i<=n;i++)
	{
		p+=a[i];
		if (p>lim) return 0;
		if (p==lim) p=0;
	}
	return 1;
}

int main()
{
	scanf("%d",&T);
	while (T--)
	{
		scanf("%d",&n);
		sum=0;
		for (int i=1;i<=n;i++)
		{
			scanf("%d",&a[i]);
			sum+=a[i];
		}
		ans=n;
		int k=trunc(sqrt(sum));
		for (int i=1;i<=k;i++)
		{
			if (sum%i==0)
			{
				if (check(i)) ans=min(ans,n-sum/i);
				if (check(sum/i)) ans=min(ans,n-i);
			}
		}
		printf("%d\n",ans);
	}
}

E.
考虑枚举最小值,并记录每个值的数的个数,并预处理出前缀和 s u m i sum_i sumi
那么以 x x x为最小值的合法答案为 ( s u m x + k − s u m x − 1 m ) − ( s u m x + k − s u m x m ) \binom{sum_{x+k}-sum_{x-1}}{m}-\binom{sum_{x+k}-sum_{x}}{m} (msumx+ksumx1)(msumx+ksumx)
这样就保证了最小值为 x x x,就不会算重。
E1因为确定了 m m m k k k,但不能取模,直接把组合数拆开即可。
E2预处理阶乘与逆元即可。

代码:
E1(用上面的公式即可,不需要像代码一样分类讨论).

#include <iostream>
#include <cstdio>
#include <cmath>
#define LL long long

const int maxn=2e5+7;

using namespace std;

int n,T,x;
int a[maxn];
LL ans;

int main()
{
	scanf("%d",&T);
	while (T--)
	{
		scanf("%d",&n);
		for (int i=1;i<=n;i++) a[i]=0;
		for (int i=1;i<=n;i++)
		{
			scanf("%d",&x);
			a[x]++;
		}
		ans=0;
		for (int i=1;i<=n;i++)
		{
			if (a[i]>=3) ans+=(LL)a[i]*(LL)(a[i]-1)*(LL)(a[i]-2)/6;
			if ((i<n) && (a[i]>1) && (a[i+1]>=1)) ans+=(LL)a[i]*(LL)(a[i]-1)*(LL)a[i+1]/2;
			if ((i<n) && (a[i]>=1) && (a[i+1]>1)) ans+=(LL)a[i]*(LL)(a[i+1]-1)*(LL)a[i+1]/2;
			if ((i<n-1) && (a[i]>1) && (a[i+2]>=1)) ans+=(LL)a[i]*(LL)(a[i]-1)*(LL)a[i+2]/2;
			if ((i<n-1) && (a[i]>=1) && (a[i+2]>1)) ans+=(LL)a[i]*(LL)(a[i+2]-1)*(LL)a[i+2]/2;
			if ((i<n-1) && (a[i]>=1) && (a[i+1]>=1) && (a[i+2]>=1)) ans+=(LL)a[i]*(LL)a[i+1]*(LL)a[i+2];
		}
		printf("%lld\n",ans);
	}
}

E2.

#include <iostream>
#include <cstdio>
#include <cmath>
#define LL long long

const int maxn=2e5+7;
const LL mod=1e9+7;

using namespace std;

int n,T,x,m,k;
int a[maxn],sum[maxn];
LL jc[maxn],inv[maxn];
LL ans;

LL ksm(LL x,LL y)
{
	if (y==1) return x;
	LL c=ksm(x,y/2);
	c=(c*c)%mod;
	if (y&1) c=(c*x)%mod;
	return c;
}

LL C(int n,int m)
{
	if (n<m) return 0;
	return jc[n]*inv[m]%mod*inv[n-m]%mod;
}

int main()
{
	scanf("%d",&T);
	while (T--)
	{
		scanf("%d%d%d",&n,&m,&k);
		for (int i=1;i<=n;i++) a[i]=0;
		for (int i=1;i<=n;i++)
		{
			scanf("%d",&x);
			a[x]++;
		}
		jc[0]=1;
		for (int i=1;i<=n;i++)
		{
			sum[i]=sum[i-1]+a[i];
			jc[i]=jc[i-1]*(LL)i%mod;
		}
		inv[n]=ksm(jc[n],mod-2);
		for (int i=n-1;i>=0;i--) inv[i]=inv[i+1]*(LL)(i+1)%mod;
		
		ans=0;
		int p;
		for (int i=1;i<=n;i++)
		{
			if (i+k>n) p=n;
			      else p=i+k;
			if (sum[p]-sum[i-1]<m) continue;
			ans=(ans+C(sum[p]-sum[i-1],m)+mod-C(sum[p]-sum[i],m))%mod;
		}
		printf("%lld\n",ans);
	}
}

F.
我们可以枚举一个区间,再把与他相交的区间加上,那么这个集合一定是合法的。
我们可以先把区间离散化,并把区间按左边界排序,维护两个指针 l l l r r r,维护 s u m sum sum表示与 [ l , r ] [l,r] [l,r]相交的区间的个数。如果 r r r遇到一个左边界,则 s u m + 1 sum+1 sum+1;如果 l l l过了一个有边界,则 s u m − 1 sum-1 sum1。因为我们以及对区间左边界排序,而后面区间右边界如果小于前面区间的右边界,那么后面的区间就被前面的区间包含,他一定不是最优的。所以可以保证 l , r l,r l,r均单调,维护的复杂度就是 O ( n ) O(n) O(n) 。当然总复杂度是排序的 O ( n l o g n ) O(nlogn) O(nlogn)

代码:

#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>

const int maxn=4e5+7;

using namespace std;

int n,T,l,r,sum,ans;
int b[maxn],p[maxn],q[maxn];

struct rec{
	int x,y;
}a[maxn];

bool cmp(rec a,rec b)
{
	if (a.x==b.x) return a.y>b.y;
	return a.x<b.x;
}

int main()
{
	scanf("%d",&T);
	while (T--)
	{
		scanf("%d",&n);
		for (int i=1;i<=n;i++)
		{
			scanf("%d%d",&a[i].x,&a[i].y);
			b[i*2-1]=a[i].x;
			b[i*2]=a[i].y;
		}
		sort(b+1,b+n*2+1);
		int len=unique(b+1,b+2*n+1)-b;
		for (int i=1;i<=2*n;i++) p[i]=q[i]=0;
		for(int i=1;i<=n;i++)
		{
			a[i].x=lower_bound(b+1,b+len,a[i].x)-b;
			a[i].y=lower_bound(b+1,b+len,a[i].y)-b;
			p[a[i].x]++;
			q[a[i].y]++;
		}
		sort(a+1,a+n+1,cmp);
		l=r=sum=ans=0;
		for (int i=1;i<=n;i++)
		{
			if ((l<=a[i].x) && (a[i].y<=r)) continue;
			while (r<a[i].y) r++,sum+=p[r];
			while (l<a[i].x) sum-=q[l],l++;
			ans=max(ans,sum);
		}
		printf("%d\n",n-ans);
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值