比赛传送门

A

Pro

传送门

Sol

考虑将前后两部分差距最大化,显然前一部分放得是前nn小的,后一部分放的是前nn大的,于是排序即可。

#include <bits/stdc++.h>
#define MAXN 2005
using namespace std;
inline int read(){
	int x=0,f=1;
	char ch=getchar();
	while (ch<'0'||ch>'9'){
		if (ch=='-') f=-1;
		ch=getchar();
	}
	while (ch>='0'&&ch<='9'){
		x=(x<<1)+(x<<3)+(ch^'0');
		ch=getchar();
	}
	return x*f;
}
int a[MAXN];
int main(){
	int n=read();
	for (register int i=1;i<=n*2;++i){
		a[i]=read();
	}
	sort(a+1,a+1+n*2);
	int sum1=0,sum2=0;
	for (register int i=1;i<=n;++i){
		sum1+=a[i];
	}
	for (register int i=n+1;i<=n*2;++i){
		sum2+=a[i];
	}
	if (sum1!=sum2) {
		for (register int i=1;i<=2*n;++i){
			printf("%d ",a[i]);
		}
	}
	else {
		puts("-1");
	}
}

B

Pro

传送门

Sol

考虑一个数aia_i要和aja_j交换,如果他们两个奇偶性不同,那么直接交换即可。
如果他们两个奇偶性相同,那么假设存在一个和他们俩奇偶性不同的aka_kswap(ai,ak),swap(ak,aj)swap(a_i,a_k),swap(a_k,a_j)即可。

这样我们在不影响整个序列其他数的情况下交换了两个数。

再考虑选择排序,只要能交换序列中任意两个数,就可以将序列变得有序,于是这样是可以使序列从小到大排序的。

什么时候不行?序列里面只有一种奇偶性,此时输出原序列。

#include <bits/stdc++.h>
#define MAXN 100005
using namespace std;
inline int read(){
	int x=0,f=1;
	char ch=getchar();
	while (ch<'0'||ch>'9'){
		if (ch=='-') f=-1;
		ch=getchar();
	}
	while (ch>='0'&&ch<='9'){
		x=(x<<1)+(x<<3)+(ch^'0');
		ch=getchar();
	}
	return x*f;
}
int a[MAXN];
int main(){
	int n=read();
	bool flag1=true,flag2=true;
	for (register int i=1;i<=n;++i){
		a[i]=read();
		if (!(a[i]&1)) flag1=false;
		else flag2=false;
	}
	if ((!flag1)&&(!flag2)) sort(a+1,a+1+n);//两种奇偶性都有
	for (register int i=1;i<=n;++i){
		printf("%d ",a[i]);
	}
}

C

Pro

传送门

Sol

考虑一个诡异的想法,题目要求gcd(i,j)==1gcd(i,j)==1ai!=aja_i != a_j,不妨这么想gcd(i,j)!=1gcd(i,j) != 1,我们都让ai,aja_i,a_j相等。
这样的话,我们只要把2,3,5,7,11...2,3,5,7,11...的倍数分别设成相同的数即可。

#include <bits/stdc++.h>
#define MAXN 100005
using namespace std;
inline int read(){
	int x=0,f=1;
	char ch=getchar();
	while (ch<'0'||ch>'9'){
		if (ch=='-') f=-1;
		ch=getchar();
	}
	while (ch>='0'&&ch<='9'){
		x=(x<<1)+(x<<3)+(ch^'0');
		ch=getchar();
	}
	return x*f;
}
int prime[MAXN],stk[MAXN],ans[MAXN],top;
int main(){
	int n=read();
	prime[0]=prime[1]=true;
	prime[2]=false;
	for (register int i=2;i<=n;++i){
		if (!prime[i]){
			stk[++top]=i;
			for (register int j=i*2;j<=n;j+=i){
				prime[j]=true;
			}
		}
	}
	int num=0;
	for (register int i=top;i>=1;--i){
		int p=stk[i];
		num++;
		for (register int j=p;j<=n;j+=p){
			if (!ans[j]) ans[j]=num;
		}
	}
	for (register int i=2;i<=n;++i){
		printf("%d ",ans[i]);
	}
}

D

Pro

传送门

Sol

首先,看到连续的异或和就要想到把他们转化为前缀和的异或,记sum[i]=a[1]a[2]...a[i]sum[i]=a[1] \oplus a[2] \oplus ... \oplus a[i],那么a[l]...a[r]=sum[r]a[l1]a[l] \oplus ... \oplus a[r]=sum[r] \oplus a[l-1]

转化为不存在sum[i]=sum[j]sum[i]=sum[j]或者sum[i]=xsum[j]sum[i]=x \oplus sum[j]

这该怎么搞?

有一个很玄学的贪心,考虑记录use[]use[]数组,代表sum[i]sum[i]有没有出现,依次扫过去,如果ii没有用,那么加入ii,并且标记ixi\oplus x,考虑正确性,因为ax==bxa\oplus x==b \oplus x推出a==ba==b,那么肯定不会把use[ix]use[i\oplus x]弄重复,而且ixi\oplus x一定大于ii,如果小于的话,在ixi\oplus x的地方ixx=ii \oplus x \oplus x = i已经被标记过了,矛盾。

use[0]=use[x]=1;
for (register int i=0;i<(1<<n);++i){
	if (!use[i]) use[i^x]=true,sum[++top]=i;
}

但是这样只能证明是合理的解,但是最优性不知道怎么证。

就当一个结论来记算了。

#include <bits/stdc++.h>
#define MAXN (1<<18)+5
using namespace std;
inline int read(){
    int x=0,f=1;
    char ch=getchar();
    while (ch<'0'||ch>'9'){
        if (ch=='-') f=-1;
        ch=getchar();
    }
    while (ch>='0'&&ch<='9'){
        x=(x<<3)+(x<<1)+(ch^'0');
        ch=getchar();
    }
    return x*f;
}
int sum[MAXN],use[MAXN],top;
int main(){
	int n=read(),x=read();
	use[0]=use[x]=1;
	for (register int i=0;i<(1<<n);++i){
		if (!use[i]) use[i^x]=true,sum[++top]=i;
	}
	printf("%d\n",top);
	for (register int i=1;i<=top;++i){
		printf("%d ",sum[i]^sum[i-1]);
	}
}