[CF1372A-E] Codeforces Round #655(Div. 2)

快放假了,也许更博会频繁点了
做完前两个,发现这场又评测排队时间过长就unr了,又因为B犯了个sb错误导致很不爽,于是就去划水了
期间又口胡了C,结果并没对

CF1372A Omkar and Completion

http://codeforces.com/problemset/problem/1372/A
无脑题,输出 (n)(1) 就行了,当然输出别的数也行
交之前还犹豫了会,觉得2A不能这么简单啊

int main(){int T=read();while(T--){
	int n=read();
	while(n--) printf("1 ");EN;
}
	return 0;
}

CF1372B Omkar and Last Class of Math

http://codeforces.com/problemset/problem/1372/B
给定 (tle 10)(n,nle 10^9),求 (a,b) 满足 (a+b=n,operatorname{lcm}(a,b)) 最小

(a=ik,b=jk,gcd(i,j)=1),则 (operatorname{lcm}(a,b)=ijk)
于是枚举 (n) 的因数 (k),那么 (i+j=frac{n}{k})

为了让 (icdot j) 最小,就让 (|i-j|) 最大,也就是 (i=1,j=frac{n}{k}-1)
这样恰好也保证了 (gcd(i,j)=1)
更新答案同时记录下 (ik,jk) 的最优取值就行了

int main(){int T=read();while(T--){
	LL n=read();
	LL max=std::sqrt(n);
	LL ans=1e18,ij,ans1,ans2;
	for(reg LL k=1;k<=max;k++)if(!(n%k)){
		ij=n/k;
		if(ij==1) goto NN;
		if(ans>k*(ij-1)){
			ans=k*(ij-1);
			ans1=k;ans2=(ij-1)*k;
		}
		NN:;
		k=n/k;
		ij=n/k;
		if(ij==1) goto NNN;
		if(ans>k*(ij-1)){
			ans=k*(ij-1);
			ans1=k;ans2=(ij-1)*k;
		}
		NNN:;
		k=n/k;
	}
	printf("%lld %lld
",ans1,ans2);
}
	return 0;
}

CF1372C Omkar and Baseball

http://codeforces.com/problemset/problem/1372/C
给定一个排列,每次操作可以选定一个连续区间,交换区间内任意两数任意次,并使得最终交换完成后,区间内所有的数与原来的位置不同
问最小经过几次这样的操作,能把排列变成有序的

当序列本来就有序,次数为 (0)

当序列有一部分的数在正确的位置((a_i=i)),而不在正确位置的数全部集中在一个连续序列
也就是说对于 (1le lle rle n)(forall lle ile r,a_i eq i,forall 1le i<l,a_i=i,forall r<ile n,a_i=i)
则只需要一次操作,就是交换区间 ([l,r]) 使之有序

对于其它情况,需要两次
第一次操作我们把所有数都交换到错误的位置,使得 (a_i eq i)(这样显然是可行的)
第二次,就可以把他们都交换成有序了

int a[200005],n;
inline int check_0(){
	for(reg int i=1;i<=n;i++)if(a[i]!=i) return 0;
	return 1;
}
inline int check_1(){
	reg int i=1;
	while(a[i]==i&&i<=n) i++;
	while(a[i]!=i&&i<=n) i++;
	while(a[i]==i&&i<=n) i++;
	return i>n;
}
int main(){int T=read();while(T--){
	n=read();
	for(reg int i=1;i<=n;i++) a[i]=read();
	if(check_0()) puts("0");
	else if(check_1()) puts("1");
	else puts("2");
}
	return 0;
}

CF1372D Omkar and Circle

http://codeforces.com/problemset/problem/1372/D
给一个序列,长度为奇数,首尾相接拼成一个环,每次选一个数,将他变成他相邻两数的和,并删去相邻两数
直到只剩一个数,求最终剩下这个数的最大值

可以想到,最终的结果肯定是这个环上 (frac{n+1}{2}) 个不同的数的和
然后发现,在最优情况时,这些数中,除了两个数是相邻的外,剩下的都是中间隔一个数(CF官方题解好像写的是所有情况,其实可以想到并不是所有情况都是这样)

先说明存在这样的情况,也就是我选了 (i),那么 (a_i) 变成 (a_{i-1}+a_{i+1}),也就是取了 (i-1,i+1)
这时只要再选 (i+2)(在原来的编号下)就行了
然后一直这样取下去,最后剩三个元素,其中一个是我们之前所有已经决定要取的数的累加,还有两个是原数列中的数
此时只要把那个累加起来的数和两个原数列中的数的其中一个加起来作为答案就行了
所以可以通过展开环变成序列,以及隔一个数加一起的“前缀和”来处理所这种情况的最优解就行了,具体看代码

那么再说明为什么整个题的最优解一定在这种情况中(也就是每隔一个取一个,一共取 (frac{n+1}{2}) 个的情况)
比如说我有 (5) 个元素,第一次选 (a_3),第二次还是选 (a_3),此时是合并后的 (a_3,a_3=a_2+a_4),那么最终答案就是 (a_1+a_5)
但显然这样并不是最优,因为完全可以像之前那样,取到 (a_1+a_5+a_3)
也就是说,如果不按上面讲的方式取,一定会多“丢弃”几个数,造成答案不优

LL a[400005];
int main(){
	int n=read();
	for(reg int i=1;i<=n;i++) a[i]=a[i+n]=read();
	for(reg int i=3;i<=n*2;i++) a[i]+=a[i-2];
	LL ans=0;
	for(reg int i=n+1;i<=2*n;i++) ans=std::max(ans,a[i]-a[i-n-1]);
	printf("%lld",ans);
	return 0;
}

CF1372E Omkar and Last Floor

http://codeforces.com/contest/1372/problem/E
(n imes m) 的格子,对于每一行,分成 (k_i) 个首尾相接的区间,每个区间内找任意一格填数字 (1),其余都是 (0)
(q_i) 为第 (i) 纵列上所有数字的和,最大化 (sum_{i=1}^m q_i)

区间dp
首先注意到是平方和,((a+b)^2le a^2+b^2),所以要让这些 (1) 竖着看“更集中”,也就是能集中在一个纵列里,就不分散开

那么设计状态 (f_{l,r}) 为考虑区间 ([l,r]),答案 (sum_{i=l}^r q_i^2) 的最大值
还是枚举一个转移点 (lle kle r),那么 (f_{l,r}=max{f_{l,k-1}+w(k)+f_{k+1,r}})
其中 (w(k)) 表示的是完全包含在区间 ([l,r]) 中,且经过 (k) 的区间个数。我们把这些区间的 (k) 这一列上的数设为 (1)
不取不完全包含在 ([l,r]) 中的区间,是因为如果取了,就不能像刚才说的那样转移了
而通过 (f_{l,k-1},f_{k+1,r}) 转移而来,对于这两个状态,满足完全包含在 ([l,k-1],[k+1,r]) 的区间,肯定不会经过 (k),所以不会造成为一个区间设置多个 (1) 的情况
复杂度 (O(n^4))

int n,m;
int l[105][105],r[105][105];
int f[105][105];
int main(){
	n=read();m=read();
	for(reg int k,L,R,i=1;i<=n;i++){
		k=read();
		while(k--){
			L=read();R=read();
			for(reg int j=L;j<=R;j++) l[i][j]=L,r[i][j]=R;
		}
	}
	for(reg int len=1;len<=m;len++){
		for(reg int L=1,R=len;R<=m;L++,R++)
			for(reg int num,k=L;k<=R;k++){
				num=0;
				for(reg int i=1;i<=n;i++)if(l[i][k]>=L&&r[i][k]<=R) num++;
				f[L][R]=std::max(f[L][R],f[L][k-1]+num*num+f[k+1][R]);
			}
	}
	printf("%d",f[1][m]);
	return 0;
}
原文地址:https://www.cnblogs.com/suxxsfe/p/13288469.html