cf Round #766(Div. 2)


B

Description
n*m个方格,一个人尽量往中间坐没涂色的格子,另一个人往四角坐,求涂上0,1,2,...,n*m-1个格子后两人曼哈顿距离的最大值。
Solution
看到曼哈顿距离,会自然而然考虑将(x,y)对称到左上角(x',y')后的x'+y'。
按x'+y'升序排序后即为涂色的顺序。相同x'+y'涂色后的答案相同。
只剩一个格子没涂色时答案为dis((1,1),(n,m))。x'+y'减k则答案也减k。

#include
using namespace std;

const int N=100005;
struct grid{
	int x,y,ans;
}a[N];
int n,m,t,cnt;
int dis(grid a){
	return min(a.x-1,n-a.x)+min(a.y-1,m-a.y);
}
bool cmp_xy(grid a,grid b){
	return dis(a)>dis(b);
}
int main(){
	scanf("%d",&t);
	while(t--){
		scanf("%d%d",&n,&m);
		cnt=0;
		for(int i=1;i<=n;++i)
			for(int j=1;j<=m;++j){
				a[++cnt].x=i;a[cnt].y=j;
			}
		sort(a+1,a+1+cnt,cmp_xy); 
		for(int i=cnt,ans=(n-1)+(m-1);i;--i){
			if(i

C

Description
一棵树,求给每条边赋权值使任意长度\(\leq2\)的路径权值和都为质数。
Solution
由于质数的奇偶性,所以当某个点的度>2时无解。
由于保证连通性,所以树是一条链。交替填入2,5即可。

#include
using namespace std;

const int N=100005;
struct graph{
	int nxt,to,w;
}e[N<<1];
int g[N],deg[N],n,t,cnt;
void adde(int x,int y){
	e[++cnt].nxt=g[x];g[x]=cnt;e[cnt].to=y;e[cnt].w=0;
}
void dfs(int u,int num){
	for(int i=g[u];i;i=e[i].nxt)
		if(!e[i].w){
			e[i].w=e[i^1].w=(num==2?5:2);
			dfs(e[i].to,e[i].w);
		}
}
int main(){
	scanf("%d",&t);
	while(t--){
		cnt=1;
		memset(g,0,sizeof(g));
		memset(deg,0,sizeof(deg));
		scanf("%d",&n);
		for(int i=1,x,y;i2){
				flag=false;break;
			}
		if(!flag){
			printf("-1\n");continue;
		}
		for(int i=1;i<=n;++i)
			if(deg[i]==1){
				dfs(i,2);
			}
		for(int i=1;i

D

Description
给定一组互不相同的数,往里面一直加入任意两个数的gcd直到不能加入为止。求加入的数的个数。
Solution
最开始想的是如何处理加入一个新数带来的变化。
后来发现结果是确定的只用考虑每个数是否会存在于结果中即可。

由于gcd(n,m)

一个数k存在的条件是存在两个数a,b,是>k的k的倍数且a/k与b/k互质,即gcd(a,b)=k。
又因为所有>k的k的倍数\(a_i\)满足\(gcd(a_1,...,a_n)\geq k\),所以如果存在a,b,\(gcd(a_1,...,a_n)= k\)

埃氏筛处理即可。

#include
using namespace std;

const int N=1000001;
int n,ans;
bool b[N];
int main(){
	scanf("%d",&n);
	for(int i=1,k;i<=n;++i){
		scanf("%d",&k);
		b[k]=true;
	}
	for(int i=N-1;i;--i){
		int g=0;
		for(int j=i;j

E

Description
一栋楼,同层之间可以平移,会损失体力,不同层之间可以到特定位置走梯子,会收获体力(梯子只能往上走)。求(1,1)到(n,m)的最小损失体力值。
Solution
离散化所有梯子的起点和终点以及(1,1)和(n,m)。
dis[i]表示到达i点的最小损失体力值。
很显然的是,不同层之间独立,从下往上考虑每一层。
每一层中,一个点到另一个点直达最优,所以可以两个方向分别扫一遍求出dis[i]。
然后再更新以这一层为起点的梯子能到达的dis[i]。

#include
using namespace std;
typedef long long ll;
const int N=100005;
const ll INF=1e18;
struct ladder{
	int a,b,c,d,h;
}a[N];
int x[N],n,m,k,t,pn;
ll dis[N<<1];
pair p[N<<1];
bool cmp(ladder a,ladder b){
	return a.a x){
	int l=1,r=pn,mid;
	while(l>1;
		if(p[mid]=i;--l)
			 	if(dis[l+1]