Codeforces Round #394 (Div. 2) 题解

无需吟唱,直接传送

problem A

题目大意

已知有n个偶数,m个奇数,问这些数有没有可能组成一个严格递增1的序列

题解

判断abs(n,m) <= 1即可,注意n,m均为0的情况.

Code

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
inline void read(int &x){
	x=0;char ch;bool flag = false;
	while(ch=getchar(),ch<'!');if(ch == '-') ch=getchar(),flag = true;
	while(x=10*x+ch-'0',ch=getchar(),ch>'!');if(flag) x=-x;
}
inline int cat_max(const int &a,const int &b){return a>b ? a:b;}
inline int cat_min(const int &a,const int &b){return a<b ? a:b;}
int main(){
	int a,b;read(a);read(b);
	printf(abs(a-b) <= 1 && !(a == 0 && b == 0) ?  "YES" : "NO");
	getchar();getchar();
	return 0;
}

problem B

题目大意

在长度为m的圆形跑道上,起点不同的两个人逆时针沿跑道跑一圈,得到了每个人到它遇到的第1,2,...,n个障碍物的距离的数组。给出这两个数组,判断两个人是不是使用的一个跑道。(所有的跑道长度均为m,但不同跑道障碍物放置不同)

题解

我们要判断障碍物的放置是否相同
那么我们直接判断障碍物之间的间隔是否相同即可
由于这是个圆形跑道,所以数组[2,3,6]和[3,6,2]也是相同的
那么我们(O(n^2))枚举即可

Code

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
inline void read(int &x){
	x=0;char ch;bool flag = false;
	while(ch=getchar(),ch<'!');if(ch == '-') ch=getchar(),flag = true;
	while(x=10*x+ch-'0',ch=getchar(),ch>'!');if(flag) x=-x;
}
inline int cat_max(const int &a,const int &b){return a>b ? a:b;}
inline int cat_min(const int &a,const int &b){return a<b ? a:b;}
const int maxn = 128;
int a1[maxn],a2[maxn],b1[maxn],b2[maxn];
int n;
inline int id(int x){
	if(x > n) x -= n;
	return x;
}
int main(){
	int m;read(n);read(m);
	for(int i=1;i<=n;++i) read(a1[i]);
	for(int i=1;i<=n;++i) read(a2[i]);
	for(int i=1;i<n;++i){
		b1[i] = a1[i+1] - a1[i];
	}b1[n] = m - (a1[n] - a1[1]);
	for(int i=1;i<n;++i){
		b2[i] = a2[i+1] - a2[i];
	}b2[n] = m - (a2[n] - a2[1]);
	//for(int i=1;i<=n;++i) printf("%d %d
",b1[i],b2[i]);
	bool flag = false;
	for(int i=1,k;i<=n;++i){
		for(int j=1;j<=n;++j){
			if(b1[i] == b2[j]){
				for(k=1;k<n;++k){
					if(b1[id(i+k)] != b2[id(j+k)]) break;
				}
				if(k == n) flag = true;
			}
		}
	}
	if(flag) puts("YES");
	else puts("NO");
	getchar();getchar();
	return 0;
}

Problem C

题目大意:

给定n个长度为m的环状字符串,每个字符串的首字符上有一个指针,每次操作可将任意一个指针左移或右移1.问最少移动多少次使得所有被指针指向的字符能构成一个合法的字符串。(合法:存在数字、小写字母,特殊符号)

题解:

读了半天没读懂题,看到样例解释才明白。

我们发现我们只用在意这三种东西的存在即可
而每个字符串中只有一个指针,也就是每个字符串中只能选择一个字符
所以至少选择3个字符串
那么我们(O(n^3))枚举是哪三个字符串,并且每个字符串的指针应该指向什么

所以我们直接预处理一下每个字符串中指向每种字符的最小移动次数即可

Code

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
inline void read(int &x){
	x=0;char ch;bool flag = false;
	while(ch=getchar(),ch<'!');if(ch == '-') ch=getchar(),flag = true;
	while(x=10*x+ch-'0',ch=getchar(),ch>'!');if(flag) x=-x;
}
inline int cat_max(const int &a,const int &b){return a>b ? a:b;}
inline int cat_min(const int &a,const int &b){return a<b ? a:b;}
const int maxn = 64;
int a[maxn],b[maxn],c[maxn];
int main(){
	int n,m;read(n);read(m);
	char ch;
	for(int i=1;i<=n;++i){
		a[i] = b[i] = c[i] = 100000000;
		for(int j=0;j<m;++j){
			while(ch=getchar(),ch<'!');
			if(ch >= '0' && ch <= '9') a[i] = cat_min(a[i],cat_min(j,m-j));
			if(ch >= 'a' && ch <= 'z') b[i] = cat_min(b[i],cat_min(j,m-j));
			if(ch == '*' || ch == '#' || ch == '&') c[i] = cat_min(c[i],cat_min(j,m-j));
		}
	}
	int ans = 0x7f7f7f7f;
	for(int i=1;i<=n;++i){
		for(int j=1;j<=n;++j){
			for(int k=1;k<=n;++k){
				if(i == j || j == k || i == k) continue;
				ans = cat_min(ans,a[i] + b[j] + c[k]);
			}
		}
	}
	printf("%d
",ans);
	getchar();getchar();
	return 0;
}

Problem D

题目大意

有三数组(a,b,c)(c_i = b_i - a_i)满足(c_i)互不相同,且(a_i,b_i in [l,r]).现在给定数组(a)和离散化后的(c),还原出任意一个可行的(b)数组.无解输出-1.
(n leq 10^5)

题解

我们有(c_i = b_i - a_i)所以(b_i = a_i + c_i)
所以我们计算出(b)数组,再根据(l)的下边界调整,判断上边界是否超出即可

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
inline void read(int &x){
	x=0;char ch;bool flag = false;
	while(ch=getchar(),ch<'!');if(ch == '-') ch=getchar(),flag = true;
	while(x=10*x+ch-'0',ch=getchar(),ch>'!');if(flag) x=-x;
}
inline int cat_max(const int &a,const int &b){return a>b ? a:b;}
inline int cat_min(const int &a,const int &b){return a<b ? a:b;}
const int maxn = 100010;
int a[maxn],b[maxn],c[maxn];
int main(){
	int n,l,r;read(n);read(l);read(r);
	for(int i=1;i<=n;++i) read(a[i]);
	int maxx = -0x3f3f3f3f,minn = 0x3f3f3f3f;
	for(int i=1;i<=n;++i){
		read(c[i]);
		b[i] = a[i] + c[i];
		minn = cat_min(minn,b[i]);
		maxx = cat_max(maxx,b[i]);
	}
	if(maxx - minn > r - l) return puts("-1");
	int x = minn - l;
	for(int i=1;i<=n;++i){
		b[i] -= x;
		printf("%d",b[i]);
		if(i != n) putchar(' ');
		else putchar('
');
	}
	getchar();getchar();
	return 0;
}

Problem E

题目大意

定义在笛卡尔坐标系中的一棵树:所有的节点都在不同的整点坐标上,所有的边都不重叠地平行于坐标轴上且边不可交叉。给定一棵n((n leq 30))个节点树,问它是否能转化成笛卡尔坐标系中的树。如果能则给出所有点的坐标,必须满足(x_i,y_i leq 10^{18})

题解

很明显,每个节点的度数不能超过4.
我们可以证明在坐标域为([-10^{18},10^{18}])时只要满足度数要求,一定存在可行解。
很明显:现在最主要的是节点之间的间接冲突使得构造失败。
(如果你懂围棋的话,可以把出现冲突时的这种形状叫做愚形)
所以我们把节点之间的间距拉到足够大,使其不可能对彼此产生影响。
所以我们构造的前两个点的间距拉到(2^{58})即可
然后每次构造下一个点的时候把间距变为原来的一半
这样即使中间再装下剩下的28个点也不会互相产生影响
这样构造即可

Code

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
inline void read(int &x){
	x=0;char ch;bool flag = false;
	while(ch=getchar(),ch<'!');if(ch == '-') ch=getchar(),flag = true;
	while(x=10*x+ch-'0',ch=getchar(),ch>'!');if(flag) x=-x;
}
inline int cat_max(const int &a,const int &b){return a>b ? a:b;}
inline int cat_min(const int &a,const int &b){return a<b ? a:b;}
const int maxn = 64;
int G[maxn][11],fa[maxn];
ll x[maxn],y[maxn];
int dx[] = {0,1,-1,0,0};
int dy[] = {0,0,0,1,-1};
inline int indx(int i){
	if(i == 1) return 2;
	if(i == 2) return 1;
	if(i == 3) return 4;
	if(i == 4) return 3;
}
void dfs(int u,ll d,int k){
	for(int i=1;i<=G[u][0] && k;++i){
		if(G[u][i] == fa[u]) swap(G[u][i],G[u][indx(k)]);
	}
	for(int i=1;i<=G[u][0];++i){
		if(G[u][i] == fa[u]) continue;
		x[G[u][i]] = x[u] + d*dx[i];
		y[G[u][i]] = y[u] + d*dy[i];
		fa[G[u][i]] = u;
		dfs(G[u][i],d>>1,i);
	}
}
int main(){
	int n;read(n);
	for(int i=1,u,v;i<n;++i){
		read(u);read(v);
		G[u][++G[u][0]] = v;if(G[u][0] == 5) return puts("NO");
		G[v][++G[v][0]] = u;if(G[v][0] == 5) return puts("NO");
	}
	x[1] = y[1] = 0;fa[1] = 1;
	dfs(1,1LL<<58,0);puts("YES");
	for(int i=1;i<=n;++i) printf("%lld %lld
",x[i],y[i]);
	getchar();getchar();
	return 0;
}

Problem F

题目大意

给定一个(n*m)的初始字符矩阵,有(q)次操作,每次操作在初始矩阵上把((a_i,b_i))((c_i,d_i))之间的所有字符全部覆盖为(c_i)。两字符的距离定义为(|ch_1-ch_2|),矩阵的距离定义为对应字符距离之和。在所有操作产生的序列中取一个序列,使这个序列和其他序列的距离的总和最小。
(n,m leq 10^3 ; q leq 3*10^5)

题解

好麻烦啊这题。。真佩服在比赛中做出来的神犇们...
首先我们尝试枚举所求矩阵.
现在只要我们能在(< O(n))的时间内求出答案即可.
这也能做?你是不是来搞笑的!
我们发现,这时我们可以再枚举一边所有的其它矩阵,挨个求距离
但是我们有两个性质没有利用

  • 某一个范围内的所有字符全部相同
  • 另一部分的字符完全是初始矩阵的字符

怎么利用这些性质呢?
我们可以想到分别处理出26个英文字母在所有操作产生的矩形内匹配时对距离总和做出的贡献
这样可以利用前缀和的方式O(26)计算第一条性质中表述的范围
但是我们预处理贡献时,算贡献应该和其他所有的操作产生的矩阵匹配
所以我们还应该预处理出在所有操作产生的的矩阵中某一个字母出现在某一点的次数
然后做前缀和也可以做到O(26)查询。
然后我们在处理出初始矩阵在所有操作产生的矩阵中的距离之和。

进行查询的时候,我们也需要减去我们当前枚举的操作矩阵对刚才的预处理值做出的贡献。

Code

代码注释:
(f[i][j][k])为字母(k)出现在((i,j))的次数的前缀和,程序中先是用二维差分处理了区间加法和单点求值,然后求前缀和还原了数组,然后再一次求前缀和实现区间查询。
(num[i][j])为点((i,j))被操作矩阵的字符覆盖的次数
(g[i][j])为原序列((i,j))和其他所有操作矩阵距离和,程序中求了前缀和.

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
template<typename T>inline void read(T &x){
	x=0;char ch;bool flag = false;
	while(ch=getchar(),ch<'!');if(ch == '-') ch=getchar(),flag = true;
	while(x=10*x+ch-'0',ch=getchar(),ch>'!');if(flag) x=-x;
}
inline ll cat_max(const ll &a,const ll &b){return a>b ? a:b;}
inline ll cat_min(const ll &a,const ll &b){return a<b ? a:b;}
const int maxn = 1024;
const int maxm = 300010;
ll s[maxn][maxn],num[maxn][maxn];
ll g[maxn][maxn];
ll f[maxn][maxn][28];

struct Node{
	ll x1,y1,x2,y2;
	int ch;
}a[maxm];
ll sum = 0;
int main(){
	int n,m,q;read(n);read(m);read(q);
	char ch;
	for(int i=1;i<=n;++i){
		for(int j=1;j<=m;++j){
			while(ch=getchar(),ch<'!');
			s[i][j] = ch - 'a';
		}
	}
	for(int i=1;i<=q;++i){
		read(a[i].x1);read(a[i].y1);
		read(a[i].x2);read(a[i].y2);
		while(ch=getchar(),ch<'!');
		a[i].ch = ch - 'a';
		f[a[i].x1][a[i].y1][a[i].ch]++;
		f[a[i].x2+1][a[i].y1][a[i].ch]--;
		f[a[i].x1][a[i].y2+1][a[i].ch]--;
		f[a[i].x2+1][a[i].y2+1][a[i].ch]++;
	}
	for(int i=1;i<=n;++i){
		for(int j=1;j<=m;++j){
			for(int k=0;k<26;++k){
				f[i][j][k] += f[i-1][j][k] + f[i][j-1][k] - f[i-1][j-1][k];
				num[i][j] += f[i][j][k];
			}
		}
	}
	for(int i=1;i<=n;++i){
		for(int j=1;j<=m;++j){
			f[i][j][s[i][j]] += q - num[i][j];
		}
	}
	for(int i=1;i<=n;++i){
		for(int j=1;j<=m;++j){
			for(int k=0;k<26;++k){
				g[i][j] += f[i][j][k]*abs(s[i][j] - k);
			}sum += g[i][j];
		}
	}
	//printf("%lld
",sum);
	for(int i=1;i<=n;++i){
		for(int j=1;j<=m;++j){
			for(int k=0;k<26;++k){
				f[i][j][k] += f[i-1][j][k] + f[i][j-1][k] - f[i-1][j-1][k];
			}g[i][j] += g[i-1][j] + g[i][j-1] - g[i-1][j-1];
		}
	}
	ll ans = 1LL<<60;
	for(int i=1;i<=q;++i){
		ll nw = sum;
		for(int k=0;k<26;++k){
			nw += abs(k - a[i].ch)*(f[a[i].x2][a[i].y2][k] - f[a[i].x1-1][a[i].y2][k] - f[a[i].x2][a[i].y1-1][k] + f[a[i].x1-1][a[i].y1-1][k]);
		}nw -= (g[a[i].x2][a[i].y2] - g[a[i].x1-1][a[i].y2] - g[a[i].x2][a[i].y1-1] + g[a[i].x1-1][a[i].y1-1]);
		ans = min(ans,nw);
	}printf("%I64d",ans);
	getchar();getchar();
	return 0;
}
  
原文地址:https://www.cnblogs.com/Skyminer/p/6359894.html