【解题报告】CF DIV2 #ROUND 714 A~D

本文回顾了Codeforces Divide by Zero 2021比赛Round 714 Division 2中的四道题目:A的峰数组操作,B的按位与序列,C的递归加记忆化,D的GCD与区间MST。作者分享了解题思路、代码实现和关键技巧,包括排列变换、位运算性质、记忆化搜索和贪心策略。
摘要由CSDN通过智能技术生成

【解题报告】CF DIV2 #ROUND 714 A~D

比赛链接
开了虚拟场,秒C卡B非常离谱,不过C写得挺快的,1000+的时候A掉了。最终排名3000+,出头一点点,最后5分钟出2000了

A. Array and Peaks

思路
先弄个排好序的1~n的数组,然后要形成一个峰就交换 a i 和 a i + 1 a_i和a_{i+1} aiai+1其中i为偶数。
然后找一下规律看最多产生几个峰,如果比这个多的话就输出-1
反之按上面方法来交换数组中的数字,交换k次即可
代码

// Problem: A. Array and Peaks
// Contest: Codeforces - Divide by Zero 2021 and Codeforces Round #714 (Div. 2)
// URL: https://codeforces.com/contest/1513/problem/A
// Memory Limit: 256 MB
// Time Limit: 1000 ms
// FishingRod

#include<bits/stdc++.h>
using namespace std;
#define endl "\n"
typedef long long LL;
typedef pair<int,int> PII;
#define MULINPUT
/*DATA & KEY
t 1 100
n 1 100
k 0 n
*/
const int N=105;
int a[N];
int T;
void solve(int C)
{
	//NEW DATA CLEAN
	memset(a,0,sizeof a);
	//NOTE!!!
	int n,k;cin>>n>>k;
	int max_peak;
	for(int i=1;i<=n;i++)a[i]=i;
	if(n&1)max_peak=(n-1)/2;
	else max_peak=(n-2)/2;
	if(k>max_peak)puts("-1");
	else
	{
		for(int i=1;i<=k;i++)
			swap(a[2*i],a[2*i+1]);
		for(int i=1;i<=n;i++)cout<<a[i]<<" ";
		cout<<endl;
	}
	
}

int main()
{
	#ifdef MULINPUT
		scanf("%d",&T);
		for(int i=1;i<=T;i++)solve(i);
	#else
		solve(1);
	#endif
	return 0;
}

B. AND Sequences

思路
好久没见到位运算相关的题目了,一见到就被乱杀啊啊啊,还好先看了眼C题,不然这次又是只A一题了
参考了这位大佬的题解

对于按位与,有如下性质
a & b < = m i n ( a , b ) … … ① a \&b<=min(a,b)……① a&b<=min(a,b)
a & b = b & a … … ② a\&b=b\&a……② a&b=b&a
a & a = a … … ③ a\&a=a……③ a&a=a
题目的good定义是找到一个排列方式,在任意位置将其分割成两半,使其按位与结果相等。
在这里插入图片描述
把这个式子展开
a [ 1 ] = a [ 2 ] & a [ 3 ] & a [ 4 ] … … a [ n ] a[1]=a[2]\&a[3]\&a[4]……a[n] a[1]=a[2]&a[3]&a[4]a[n]
a [ 1 ] & a [ 2 ] = a [ 3 ] & a [ 4 ] … … a [ n ] a[1]\&a[2]=a[3]\&a[4]……a[n] a[1]&a[2]=a[3]&a[4]a[n]
a [ 1 ] & a [ 2 ] & … … a [ n − 1 ] = a [ n ] a[1]\&a[2]\&……a[n-1]=a[n] a[1]&a[2]&a[n1]=a[n]
可以发现一点a[1]和a[n]一定在等式两边
然后根据③和②我们可以得到
a [ 1 ] = a [ 1 ] & a [ 2 ] & a [ 3 ] … … a [ n ] a[1]=a[1]\&a[2]\&a[3]……a[n] a[1]=a[1]&a[2]&a[3]a[n]
a [ n ] = a [ 1 ] & a [ 2 ] & a [ 3 ] … … a [ n ] a[n]=a[1]\&a[2]\&a[3]……a[n] a[n]=a[1]&a[2]&a[3]a[n]
根据性质①可以得到
a [ 1 ] < = a [ n ] a[1]<=a[n] a[1]<=a[n] a [ n ] > = a [ 1 ] a[n]>=a[1] a[n]>=a[1]所以 a [ 1 ] = a [ n ] a[1]=a[n] a[1]=a[n]
那么就变成我们要找两个数,他们的值为 a [ 1 ] & a [ 2 ] & a [ 3 ] … … a [ n ] a[1]\&a[2]\&a[3]……a[n] a[1]&a[2]&a[3]a[n],中间的数是随便放的所以
a n s = c n t ∗ ( c n t − 1 ) ∗ ( n − 2 ) ! ans=cnt*(cnt-1)*(n-2)! ans=cnt(cnt1)(n2)!
代码

// Problem: B. AND Sequences
// Contest: Codeforces - Divide by Zero 2021 and Codeforces Round #714 (Div. 2)
// URL: https://codeforces.com/contest/1513/problem/B
// Memory Limit: 256 MB
// Time Limit: 2000 ms
// FishingRod

#include<bits/stdc++.h>
using namespace std;
#define endl "\n"
typedef long long LL;
typedef pair<int,int> PII;
#define MULINPUT
/*DATA & KEY
t 1 1e4
n total 2 2e5
ai 0 1e9
*/
const int N=2E5+10,mod=1e9+7;
int T;
LL f[N];
void solve(int C)
{
	//NEW DATA CLEAN
	map<int,int>mp;
	//NOTE!!!
	int n;cin>>n;
	int tmp;
	for(int i=1,x;i<=n;i++)
	{
		cin>>x;
		tmp=(i==1)?x:(tmp&x);
		mp[x]++;
	}
	LL cnt=mp[tmp];
	LL ans=cnt*(cnt-1)%mod*f[n-2]%mod;
	cout<<ans<<endl;
}

int main()
{
	f[0]=1;
	for(int i=1;i<=N;i++)f[i]=f[i-1]*i%mod;
	#ifdef MULINPUT
		scanf("%d",&T);
		for(int i=1;i<=T;i++)solve(i);
	#else
		solve(1);
	#endif
	return 0;
}

C. Add One

思路
递归加记忆化
因为每一位的扩展长度计算是独立的,所以对每位递归求和。
参数为,该为数字和还需操作次数
需要注意的是,这题可能比较卡输入直接cin貌似会T(我之前string+cinT了一发,不清楚int+cin如何)
然后记忆化递归的时候需要注意负数参数的出现,因为数组下标不能为负数嘛
代码

// Problem: C. Add One
// Contest: Codeforces - Divide by Zero 2021 and Codeforces Round #714 (Div. 2)
// URL: https://codeforces.com/contest/1513/problem/C
// Memory Limit: 256 MB
// Time Limit: 1000 ms
// FishingRod

#include<bits/stdc++.h>
using namespace std;
#define endl "\n"
typedef long long LL;
typedef pair<int,int> PII;
#define MULINPUT
/*DATA & KEY
t 1 2e5
n 1 1e9
m 1 2e5
*/
const int M=2E5+10,mod=1e9+7;
int T;
LL f[11][M];
LL work(int x,int m)
{
	if(f[x][m]!=0)return f[x][m]%mod;
	if(m==0)return 1;
	if(x<9)
	{
		return f[x][m]=(work(9,max(0,m-(9-x))))%mod;
	}
	if(x==9)
	{
		return f[x][m]=(work(1,m-1)+work(0,m-1))%mod;
	}
}
void solve(int C)
{
	//NEW DATA CLEAN
	
	//NOTE!!!
	int n,m;scanf("%d%d",&n,&m);
	LL ans=0;
	while(n)
	{
		ans+=work(n%10,m);
		n/=10;
	}
	printf("%lld\n",ans%mod);
}

int main()
{
	
	#ifdef MULINPUT
		scanf("%d",&T);
		for(int i=1;i<=T;i++)solve(i);
	#else
		solve(1);
	#endif
	return 0;
}

D. GCD and MST

思路
在这里插入图片描述

看了一下题意,感觉这道是一道缝合题目。
区间GCD+区间最值+MST
好家伙我一个都不能当场写出来更别说三个了,但是DIV2的D就这么猛?不会吧,再看看题目和样例看来需要挖掘一些性质
参考了这个大佬的解题报告
MST问题首先想到对边权排序,不过这边由于操作1,所以需要记录原来坐标。
然后对把每个值当作最小值来遍历,向两边扩展,因为一直是以最小值gcd来扩展的所以并不是说只有边界可以连线,而是这个区间内的两个点都可以连线(只要包含最小值的点),那么我们利用贪心,只有当值小于原有边权P,并且没有被访问过的时候才扩展。剩下没被扩展到的就直接用边权P连起来
代码

// Problem: D. GCD and MST
// Contest: Codeforces - Divide by Zero 2021 and Codeforces Round #714 (Div. 2)
// URL: https://codeforces.com/contest/1513/problem/D
// Memory Limit: 256 MB
// Time Limit: 2000 ms
// FishingRod
//thanks:sosusosu
//https://www.bilibili.com/read/cv10846548?from=search
#include<bits/stdc++.h>
using namespace std;
#define endl "\n"
typedef long long LL;
typedef pair<int,int> PII;
#define MULINPUT
/*DATA & KEY
t 1 1e4
n total 2 2e5
p 1 1e9
ai 1 1e9
*/
int T;
const int N=2E5+10;
struct Point{int a,b;}p[N];
bool cmp(Point x,Point y){return x.a<y.a;}

bool v[N];
int a[N];

void solve(int C)
{
	//NEW DATA CLEAN
	memset(p,0,sizeof p);
	memset(v,0,sizeof v);
	//NOTE!!!
	int n;LL w;cin>>n>>w;
	int cnt=n-1;
	LL ans=0;
	for(int i=1;i<=n;i++)
	{
		cin>>a[i],p[i].a=a[i];p[i].b=i;
	}
	sort(p+1,p+1+n,cmp);
	for(int i=1;i<=n&&p[i].a<w;i++)//扩展值小于w的
	{
		if(!v[p[i].b])//没访问过的
		{
			v[p[i].b]=1;
			for(int j=p[i].b+1;j<=n&&__gcd(a[j],a[p[i].b])==a[p[i].b];j++)//原来坐标向右扩展
			{
				ans+=p[i].a,cnt--;
				if(v[j])break;
				v[j]=1;
			}
			for(int j=p[i].b-1;j&&__gcd(a[j],a[p[i].b])==a[p[i].b];j--)//原来左边向左扩展
			{
				ans+=p[i].a,cnt--;
				if(v[j])break;
				v[j]=1;
			}
		}
	}
	cout<<ans+cnt*w<<endl;	
}

int main()
{
	#ifdef MULINPUT
		scanf("%d",&T);
		for(int i=1;i<=T;i++)solve(i);
	#else
		solve(1);
	#endif
	return 0;
}

反思

A:

排列相关的先想想自然序列,然后对自然系列做变换

B:

对于跟下标有关的式子,将其完全展开列出来,观察其中不变的项,寻找规律。
按位与运算性质
a & b < = m i n ( a , b ) … … ① a \&b<=min(a,b)……① a&b<=min(a,b)
a & b = b & a … … ② a\&b=b\&a……② a&b=b&a
a & a = a … … ③ a\&a=a……③ a&a=a

C:

string 1e5读入 cf可能会TLE
写递归要防止出现负数,对于一个操作,要分别考虑操作完全起效和操作部分起效

D:

区间问题可以从当前某个值向两边扩展
最小生成树用贪心排序思想
对于一个区间对于某个性质成立,观察一下区间扩展过程中,是否也是成立的,不然容易只看到最终区间符合操作条件,忽略了区间内部也是符合的,造成操作的遗漏

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值