codechef Starters 76

Not Divisible

题意:

给你一个 \(n\) ,请你构造一个数组 \(A\) ,满足如下条件:

  • \(-500\leq A_i\leq500\)
  • \(\forall (1\leq i<j\leq n)\) ,都有\(|A_i+A_{i+1}+...+A_j|\)不能被\((j-i+1)\)整除

思路:

题面大意为我们需要构造一个数组,使得数组中任意长度 \(>1\) 的连续子数组的和都不能被其长度整除

首先我们考虑以下,对于每一个长度 \(x\) ,它的倍数为 \(x,2x,3x...kx\)

我们现在希望找一种构造方式,如果可以让其 长度为 \(x\) 的 连续子数组的\(x<\sum A_i<2x\)

那么我们就达成了目标,我们知道要使得:\(\sum A_i=x\) 或者 \(\sum A_i=2x\)

一种方式就是让长度为 \(x\) 的连续子数组每一个数都为 \(1\) 或者 \(2\)

那么我们由此可以想到一种构造方式,可不可以让其都不满足,显然我们可以采取 \(1,2,1,2,1....\) 这种构造,让 \(1\)\(2\) 相间摆放即可

这样对于\(\forall len>1\),由于我们是相间摆放:故我们长度为 \(len\) 的子数组不可能全为 \(1\) 也不可能全为 \(2\)

故一定有 \(len<\sum A_i<2len\)

所以这种构造方式可以满足题意要求

代码:

#include<bits/stdc++.h>
using namespace std;


void solve(){
	int n;
	cin>>n;
	//1 2 1 2 1 2即可
	for(int i=1;i<=n;i++){
		if(i&1)cout<<1;
		else cout<<2;
		cout<<" \n"[i==n];
	}
}

int main()
{
	
	ios::sync_with_stdio(0);
    cin.tie(0),cout.tie(0);
    cout<<fixed<<setprecision(12);
    
    int t;
    //t=1;
    cin>>t;
    while(t--){
    	solve();
	}
    
	return 0;
}

Contruct Array

题意:

给你一个 \(n\) ,请你构造一个数组 \(A\) , 满足以下条件,如果构造不出,请你输出 \(-1\)

  • \(-10^9\leq A_i \leq 10^9\)

  • \(P_i\) 表示\(A_1+A_2+...+A_i\),即数组\(A\)的前缀和

    \(S_i\) 表示\(A_n+A_{n-1}+...+A_{n-i+1}\),即数组 \(A\) 的后缀和

    要求对于\(\forall (1\leq i<n)\),若 \(i\) 是奇数,一定有\(P_i>S_i\),若 \(i\) 是偶数,一定有\(P_i<S_i\)

思路:

首先我们来分析一下什么时候无解:

\(n\) 是奇数的时候,一定无解

证明:

\(n\) 是奇数,其一定存在中心位置:\(n/2+1\)

我们不妨设\(n/2\)为奇数,那么\(n/2+1\)就为偶数

那么当 \(i=n/2\)\(i=n/2+1\) 这两个位置有:

\(a_1+a_2+..+a_{n/2}>a_n+a_{n-1}+..+a_{n-n/2+1}\)

\(a_1+a_2+..+a_{n/2}+a_{n/2+1}<a_n+a_{n-1}+..+a_{n-n/2+1}+a_{n/2+1}\) \(<=>\) \(a_1+a_2+..+a_{n/2}<a_n+a_{n-1}+..+a_{n-n/2+1}\)

显然和上一个不等式矛盾,所以 \(n\) 为奇数一定无解

\(n\) 是偶数的时候,我们尝试构造:

我们可以考虑头尾对称这样来放置数:

\(1,-2,2........-2,2,-1\)

对于\(\forall i\)是奇数,一定有 \(P_i=1\),因为前缀的\(2\)\(-2\)会被相互抵消,同理\(S_i=-1\)

对于\(\forall i\)是偶数,一定有 \(P_i=-1\)\(S_i=1\)

显然满足条件:

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=1e5+10;
int a[N];

void solve(){
	//显然奇数一定无解
	int n;
	cin>>n;
	if(n&1){
		cout<<"-1\n";
		return ;
	}	
	
	for(int i=1,j=1,sign=1;i<=n/2;i++,j=2,sign*=-1){
		a[i]=sign*j;
        a[n-i+1]=-sign*j;
	}
	for(int i=1;i<=n;i++){
		cout<<a[i]<<" \n"[i==n];
	}
}

int main()
{
	
	ios::sync_with_stdio(0);
    cin.tie(0),cout.tie(0);
    cout<<fixed<<setprecision(12);
    
    int t;
    //t=1;
    cin>>t;
    while(t--){
    	solve();
	}
	
	return 0;
}

Bitwise Operations on Circle

题意:

你有一个长度为 \(n\) 的数组 \(c\) ,他们绕成一个圈,现在你有如下两种操作:

  1. 选择两个相邻的数 \(c_i\)\(c_j\),将他们合并,结果为 \(c_i|c_j\)
  2. 选择两个相邻的数 \(c_i\)\(c_j\),将他们合并,结果为 \(c_i\&c_j\)

你需要操作恰好 \((n-2)\) 次,此时数组会剩下两个数:\(X,Y\)

你希望 \(|X-Y|\) 的值尽可能大,请问这个值最大是多少?

思路:

首先我们要知道 \(|\) 操作时越或越大,而 \(\&\) 操作是越交越小的

即我们或操作会是数非严格单增,交操作会使数非严格单减

那么我们希望 \(|X-Y|\) 尽可能的大,那么我们肯定希望其中一个数尽可能大,另一个数尽可能小

故我们发现最优解当中,\(X\) 或者 \(Y\) 一定是只属于或操作或者交操作,即我们两个操作不会混

因为我们假设先把两个数或起来,然后再去和另一个数交,那么我这个数又可能会变小,所以我们贪心的极端想

最后我们一定会用或操作形成一个数,用交操作形成另一个数

现在问题就变成了,我们需要把一些数或起来,一些数交起来

我们考虑把破环成链,把数组展开成一个长度为\(2*n\)的样子:

\(a_1,a_2....a_n,a_1.....a_n\)

这样我们只要枚举开头,连续长度为 \(n\) 的一段,就一定对应着一种合并的链(这里类似与区间dp中环形石子合并的做法)

现在由于我们刚刚的结论,所以我们肯定是这一条链中,找到一段连续的前缀用或,然后剩下的后缀用交,形成了两个数

现在我们只要快速枚举计算答案即可,同时我们需要快速知道一个区间的或和交结果,我们可以采用 \(ST\)

然后我们再发现一个性质,就是我们多个数或的结果,由于我们所有数 \(c_i<2^{30}\) ,故我们或的结果最多只有\(30\)种不同的数

这样我们枚举链之后,用二分快速找到前缀不同的或结果,然后计算答案即可

时间复杂度为:\(N(logN)^2\)

代码:

#include<bits/stdc++.h>
using namespace std;
using LL = long long; 
const int N=4e5+10;
int c[N];
int orf[N][25],andf[N][25]; //区间或,区间&
int lg2[N];

void solve(){
	int n;
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>c[i];
	}
	for(int i=1;i<=n;i++){
		c[i+n]=c[i];
	}
	for(int i=1;i<=n;i++){
	    lg2[i]=log2(i);
	}
	auto init=[&](){
		for(int i=0;i<=20;i++){
			for(int j=1;j+(1<<i)-1<=2*n;j++){
				if(!i)orf[j][i]=c[j];
				else orf[j][i]=orf[j][i-1]|orf[(1<<(i-1))+j][i-1];
			}
		}	
		for(int i=0;i<=20;i++){
			for(int j=1;j+(1<<i)-1<=2*n;j++){
				if(!i)andf[j][i]=c[j];
				else andf[j][i]=andf[j][i-1]&andf[(1<<(i-1))+j][i-1];
			}
		}
	};
	init();
	auto getor=[&](int l,int r){
		int k=lg2[r-l+1];
		return orf[l][k]|orf[r-(1<<k)+1][k];	
	};
	auto getand=[&](int l,int r){
		int k=lg2[r-l+1];
		return andf[l][k]&andf[r-(1<<k)+1][k];	
	};
	int ans=0;
	for(int i=1;i<=n;i++){
		int cur=i,L,R;
		while(1){
			ans=max(ans,abs(getor(i,cur)-getand(cur+1,i+n-1)));
			//利用二分把所有不同的前缀or找出来
			L=cur,R=i+n-1;
			while(L<R){
				int mid=(L+R+1)/2;
				if(getor(i,cur)>=getor(i,mid))L=mid;
				else R=mid-1;
			}
			cur=L+1;
			if(cur>=i+n-1)break;
		}
	}
	cout<<ans<<"\n";
	
}

int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cout<<fixed<<setprecision(12);
    
    int t;
    //t=1;
    cin>>t;
    while(t--){
    	solve();
	}
    
	
	return 0;
}

posted @ 2023-02-02 18:31  jackle  阅读(16)  评论(0)    收藏  举报