Codeforces Round #690 (Div. 3) (补题情况)

战况(+17)

A. Favorite Sequence *800

直接模拟的水题开俩指针即可

#include <bits/stdc++.h>
using namespace std;
int a[100005];
int main()
{
	int t;
	scanf("%d",&t);
	while(t--)
	{
		int n;
		scanf("%d",&n);
		for(int i=1;i<=n;i++) scanf("%d",&a[i]);
		int l=1,r=n;
		while(1)
		{
			printf("%d ",a[l]);
			l++;
			if(l>r) break;
			printf("%d ",a[r]);
			r--;
			if(l>r) break;
		}
		puts("");
	}
}

B. Last Year's Substring *800

直接被干翻了,只操作一次让我直接迷惑了(直接做了30min)。。。实际上就看前缀后缀能不能正好组成2020即可。。。

#include <bits/stdc++.h>
using namespace std;
int a[100005],b[100005];
map<int,int> mk;
int main()
{
	int t;
	scanf("%d",&t);
	while(t--)
	{
		int n,ok=0;
		scanf("%d",&n);
		for(int i=1;i<=n;i++) scanf("%1d",&a[i]);
		if(a[1]==2)
		{
			if(a[2]==0)
			{
				if(a[3]==2)
				{
					if(a[4]==0) ok=1;
					else if(a[n]==0) ok=1;
				}
				else if(a[n]==0&&a[n-1]==2) ok=1;
			}
			else if(a[n]==0&&a[n-1]==2&&a[n-2]==0) ok=1;
		}
		else if(a[n]==0&&a[n-1]==2&&a[n-2]==0&&a[n-3]==2) ok=1;
		if(ok) puts("YES");
		else puts("NO");
	}
}

C. Unique Number *900

这个直接贪心就行没啥好说的

#include <bits/stdc++.h>
using namespace std;
int a[100005],b[100005];
map<int,int> mk;
int main()
{
	int t;
	scanf("%d",&t);
	while(t--)
	{
		int x;
		scanf("%d",&x);
		if(x>45) puts("-1");
		else
		{
			if(x<=9) printf("%d\n",x);
			else if(x<=17)
			{
				int now=x-9;
				printf("%d9\n",now);
			}
			else if(x<=24)
			{
				int now=x-17;
				printf("%d89\n",now);
			}
			else if(x<=30)
			{
				int now=x-24;
				printf("%d789\n",now);
			}
			else if(x<=35)
			{
				int now=x-30;
				printf("%d6789\n",now);
			}
			else if(x<=39)
			{
				int now=x-35;
				printf("%d56789\n",now);
			}
			else if(x<=42)
			{
				int now=x-39;
				printf("%d456789\n",now);
			}
			else if(x<=44)
			{
				int now=x-42;
				printf("%d3456789\n",now);
			}
			else printf("123456789\n");
		}
	}
}

D. Add to Neighbour and Remove *1400

还是贪心直接暴力试试看看能不能分成就行

#include <bits/stdc++.h>
using namespace std;
int a[100005],b[100005];
map<int,int> mk;
int main()
{
	int t;
	scanf("%d",&t);
	while(t--)
	{
		int n,s=0;
		scanf("%d",&n);
		for(int i=1;i<=n;i++) scanf("%d",&a[i]),s+=a[i];
		for(int i=n;i>=1;i--)
		{
			if(s%i==0)
			{
				int now=0,ok=0;
				for(int j=1;j<=n;j++)
				{
					now+=a[j];
					if(now==s/i) now=0;
					if(now>s/i) 
					{
						ok=0;
						break;
					}
				}
				if(now==0) ok=1;
				if(ok)
				{
					printf("%d\n",n-i);
					break;
				}
			}
		}
	}
}

E. Close Tuples *1700

题意是给你一个\(2e5\)的数组,让你从中寻找\(m\)个数使得\(max(a_1,a_2..a_m)-min(a_1,a_2,...a_m)\)\(\leq k\) 其中题目里要保证\(i_1<i_2<i_3<...<i_m\)但是我们取最大值和最小值的时候数组就变成了无序的了,于是这个条件完全可以忽略掉。

我们可以先把原先的数组\(sort\)一下然后做一个滑动的窗口计算里面的每个数的值,注意这里还需要减去重复的数组

#include <bits/stdc++.h>
typedef long long ll;
using namespace std;
ll f[200005],mod=1e9+7,a[200005];
ll inv(ll x)
{
	ll base=x,ans=1,b=mod-2;
	while(b)
	{
		if(b&1) ans=ans%mod*base%mod;
		base=base%mod*base%mod;
		b>>=1;
	}
	return ans%mod;
}
ll C(ll n,ll m)
{
	if(m>n) return 0;
	return f[n]*inv(f[m]*f[n-m]%mod)%mod;
}
int main()
{
//	freopen("t1.out","r",stdin);
	int t;
	scanf("%d",&t);
	f[0]=1;
	for(int i=1;i<=200001;i++) f[i]=f[i-1]*i%mod;
	while(t--)
	{
		int n,m,k;
		scanf("%d%d%d",&n,&m,&k);
		for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
		sort(a+1,a+1+n);
		ll l=1,r=1,ans=0,last=0;
		while(1)
		{
			while(a[r]-a[l]<=k&&r<=n) r++;
			ans=ans%mod+C(r-l,m)%mod,ans%=mod;
			ans=ans%mod-C(last,m)%mod,ans%=mod;
//			cout<<ans<<' '<<l<<" "<<r<<"\n";
			if(r==n+1) break;
			while(a[r]-a[l]>k) l++;
			ans=ans%mod+C(r-l+1,m)%mod,ans%=mod;
			ans=ans%mod-C(r-l,m)%mod,ans%=mod;
			last=r-l+1;
//			cout<<ans<<' '<<l<<" "<<r<<"\n";	
		}	
		printf("%lld\n",(ans%mod+mod)%mod);
	}	
} 

或者按照lsr的解法每次添加一个数就把他当做最后一个数求前面的\({C_s}^{m-1}\)的加和也是答案,这样不用减法去重更加简单

#include <bits/stdc++.h>
typedef long long ll;
using namespace std;
ll f[200005],mod=1e9+7,a[200005];
ll inv(ll x)
{
	ll base=x,ans=1,b=mod-2;
	while(b)
	{
		if(b&1) ans=ans%mod*base%mod;
		base=base%mod*base%mod;
		b>>=1;
	}
	return ans%mod;
}
ll C(ll n,ll m)
{
	if(m>n) return 0;
	return f[n]*inv(f[m]*f[n-m]%mod)%mod;
}
int main()
{
//	freopen("t1.out","r",stdin);
	int t;
	scanf("%d",&t);
	f[0]=1;
	for(int i=1;i<=200001;i++) f[i]=f[i-1]*i%mod;
	while(t--)
	{
		int n,m,k;
		scanf("%d%d%d",&n,&m,&k);
		for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
		sort(a+1,a+1+n);
		ll l=1,r=1,ans=0;
		while(1)
		{
			while(a[r]-a[l]<=k&&r<=n) ans=ans%mod+C(r-l,m-1)%mod,ans%=mod,r++;
			if(r==n+1) break;
			while(a[r]-a[l]>k) l++;	
		}	
		printf("%lld\n",(ans%mod+mod)%mod);
	}	

F. The Treasure of The Segments *1800

给你\(2e5\)条线段找其中一条线段与其他线段不相交的数量的最小值。

处理线段的话把左端点放到\(l[]\)数组内右端点放到\(r[]\)数组内,然后把他们都\(sort\)一下保证升序

要看一条线段与几条线段相交直接判断比较费劲,可以减去与他不相交的线段的数量即可。

不相交的数量怎么算呢?

实际上不相交只有两种情况一种是某条线段的右端点小于我这条线段的左端点那么我们就不相交,另一种情况是某条线段的左端点大于我这条线段的右端点我们就不相交,两个的加和就是与其他线段不相交的数量,\(for\)循环遍历一下求最小值就可,中间套上二分,复杂度为\(O(NlogN)\)

二分的话我拿出我这条线段的左端点去\(lower\_bound\ r[]\)数组然后找到的第一个位置\(-1\)就是第一种情况

然后我拿这条线段的右端点去\(upper\_bound\ l[]\)数组找到的第一个位置就是第二种情况,这里需要用\(n-ans+1\)来算出第二种情况总和

#include <bits/stdc++.h>
using namespace std;
int l[200005],r[200005],L[200005],R[200005];
void solve()
{
	int n,p=0;
	scanf("%d",&n);
	int ans=n-1;
	for(int i=1;i<=n;i++)
	{
		scanf("%d%d",&l[i],&r[i]);
		L[i]=l[i];
		R[i]=r[i];
	}
	sort(L+1,L+1+n);
	sort(R+1,R+1+n);
	for(int i=1;i<=n;i++)
	{
		int now=0;
		now+=lower_bound(R+1,R+1+n,l[i])-R-1;
		now+=n-(upper_bound(L+1,L+1+n,r[i])-L)+1;
		ans=min(ans,now);
	}
	printf("%d\n",ans);
}
int main()
{
	int T;
	scanf("%d",&T);
	while(T--) solve();
}

F(extra) Interaction *2000

给定\(2e5\)条已经存在的线段,询问\(2e5\)次每次给定两个数\(l_i\ r_i\ l_i\leq r_i\)代表一条线段,询问在这个已经存在的线段集合中有几条线段与他相交,每次询问的线段不加入线段的集合中。做法同上。

小结

这场比赛也是好久没有打了的第一场,没想到第二题直接做了小半小时,想复杂了,后面的话E2也不大会做,赛后补了也小半天可算用加加减减把他做出来了,实际上还有更简便的方法,F题就是线段处理的基础题,其中的方法应该完全掌握。

posted @ 2020-12-16 22:58  baccano!  阅读(122)  评论(0编辑  收藏  举报