【补题】The 2024 ICPC Kunming Invitational Contest E. Relearn through Review

题意:给出两个数n,k,然后给定一个长度为n的整数序列,最多一次操作,可以使任何长度的连续区间加上k,问怎么让这个序列的gcd最大。

思路:有点剪枝的枚举。

1.很明显不存在什么一个公式可以直接得到结果的gcd,因此想到了枚举。

2.如果每次枚举都要进行计算,那么时间复杂度会极大,并且在仔细观察题目,最多一次操作,并且对象是连续的区间,其实可以对区间进行预处理。因为可以看作3个区间的最终结果,前缀+操作区间+后缀,3个区间是>=0长度的。我使用了前缀后缀gcdST表维护所有操作区间。

3.关键点是因为前缀数组或者后缀数组的第一个值是不可避免被选上的,那么gcd只能越来越小,不存在超越值本身,包括与后面的值进行计算,因此枚举的对象是前缀gcd要减小的瞬间和后续的分割点(即l,r两个分割点,然后对l进行了剪枝)。时间复杂度变成了nlog(第一个数的数值)
因为接下来左区间只可能出现数值的因数

Tips:这里对为什么左边区间只要枚举前缀gcd要减小的瞬间进行说明,这里说一种理解方式
当右指针固定的时候,左边区间如果包含越多对不影响gcd的数字,就会使中间gcd有可能越大,给出一个例子,k=2
9 9 9 9 4 4 4 4,所有4是操作区间,9是前缀gcd区间,如果将一个9分配到操作区间会导致gcd变1
9 9 9 18 2 2 2 2,所有2是操作区间,18之前是gcd区间,如果将18分配到操作区间不导致gcd变化
有没有get到一点,在前缀gcd没有变小的情况下,操作区间数字越少,变小的可能性越小,这是贪心的关键

代码:   结果代码如果写的方式够好,可以不用st表维护中间区间,st表并不是必须的

#include
using namespace std;
#define int long long
#define endl '\n'
#define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
const int N = 3e5+10;
const int INF = 1e15;
const int MOD = 1e9+7;

int f[N][25];
int ansf[N];
int ansb[N];

int val(int l,int r){
	int k=log2(r-l+1);
	return gcd(f[l][k],f[r-(1<> n >> m;
	vector ve(n+1);
	for(int i=1;i<=n;i++){
//		cin >> f[i][0];
		cin >> ve[i];
	}
	
	//只是前缀和ST表的初始化,可以跳过别看,写的太烂
	ansf[1]=ve[1];
	ansb[n]=ve[n];
	f[1][0]=ve[1]+m;
	ansb[n+1]=0;
	ansf[0]=0;
	ansb[0]=0;
	ansf[n+1]=0;
	
	for(int i=2;i<=n;i++){
		ansf[i]=gcd(ve[i],ansf[i-1]);
		f[i][0]=ve[i]+m;
	}
	for(int i=n-1;i>=1;i--){
		ansb[i]=gcd(ve[i],ansb[i+1]);
	}
	//前缀和与ST表初始化
	
	//ST表
	int maxans=1;
	for(int j=1;j<=25;j++){
		for(int i=1;i+(1<i;j--){
			nowon=gcd(now,ansb[j+1]);
			maxans=max(maxans,gcd(nowon,val(i+1,j)));
		}
	}
	
	cout << maxans << endl;
}

signed main(){
	IOS;
	
	int t=1;
	cin >> t;
	while(t--){
		solve();
	}
	
}

你可能感兴趣的:(算法,数据结构)