Educational Codeforces Round 132 div.2 A-F题解

视频讲解:TBD

A. Three Doors

题目大意

3 3 3 个门和 3 3 3 把对应的钥匙。其中 2 2 2 把钥匙分别在 2 2 2 扇门后, 1 1 1 把在手上。打开门才能获得门后的钥匙,问能否打开所有的门。

题解

判断前两次开的门后,是否有钥匙即可。

参考代码

#include
using namespace std;
typedef long long ll;

int main()
{
	int T,x,a[5],now;
	scanf("%d",&T);
	while(T--)
	{
		scanf("%d%d%d%d",&x,&a[1],&a[2],&a[3]);
		now=3^2^1^a[1]^a[2]^a[3];
		if(a[now]==0||a[a[now]]==0)
			printf("NO\n");
		else
			printf("YES\n");
	}
}

B. Also Try Minecraft

题目大意

有长为 n n n 列的世界,第 i i i 列高 a i a_i ai 。每一步可以向左或向右移动一格,若移动到更低处,则会受到高度差的伤害。
q q q 次询问,每次询问求从低 s s s 列移动第 j j j 列会受到多少伤害。

题解

预处理受到伤害的前缀和与后缀和,每次询问时求差即可得到答案。
p r e i = p r e i − 1 + m a x ( 0 , a i − 1 − a i ) pre_i=pre_{i-1}+max(0,a_{i-1}-a_i) prei=prei1+max(0,ai1ai)

s u f i = s u f i + 1 + m a x ( 0 , a i + 1 − a i ) suf_i=suf_{i+1}+max(0,a_{i+1}-a_i) sufi=sufi+1+max(0,ai+1ai)

a n s = { p r e t − p r e s t ≥ s s u f t − s u f s t < s ans=\begin{cases} pre_t-pre_s &t\geq s \\ suf_t-suf_s &tans={pretpressuftsufstst<s

注意开long long。

参考代码

#include
using namespace std;
typedef long long ll;

const int MAXN=100100;
ll a[MAXN],pre[MAXN],suf[MAXN];

int main()
{
	int n,m,i,s,t;
	scanf("%d%d",&n,&m);
	for(i=1;i<=n;i++)
	{
		scanf("%lld",&a[i]);
	}
	for(i=2;i<=n;i++)
		pre[i]=pre[i-1]+max(0ll,a[i-1]-a[i]);
	for(i=n-1;i>=1;i--)
		suf[i]=suf[i+1]+max(0ll,a[i+1]-a[i]);
	while(m--)
	{
		scanf("%d%d",&s,&t);
		if(t>s)
			printf("%lld\n",pre[t]-pre[s]);
		else
			printf("%lld\n",suf[t]-suf[s]);
	}
}

C. Recover an RBS

题目大意

给定带未知符’?'的括号序列,每个"?“表示为”(“或”)",求是否存在唯一方案得到合法的括号序列

题解

从左到右遍历,维护以下两个初始为 0 0 0 的变量:

  • dep:括号序列的深度。遇到"(" 则 + 1 +1 +1 ,遇到")" 则 − 1 -1 1
  • now:未确定的"?" 数量。

遍历时,存在以下两个特殊情况:

  • d e p < 0 dep<0 dep<0 ,则之前必定有个未确定的"?“需要为”(",即 d e p + + , n o w − − dep++,now-- dep++,now
  • d e p = 0 , n o w = 1 dep=0,now=1 dep=0,now=1 ,则之前唯一的未确定的"?“必须为”(",即 d e p + + , n o w − − dep++,now-- dep++,now

最终判断 ∣ d e p ∣ = n o w |dep|=now dep=now 是否成立即可。

参考代码

#include
using namespace std;
typedef long long ll;

const int MAXN=200200;
char s[MAXN];

int main()
{
	int T,now,dep,i,len;
	scanf("%d",&T);
	while(T--)
	{
		scanf("%s",&s);
		len=strlen(s);
		now=0;
		dep=0;
		for(i=0;i<len;i++)
		{
			if(s[i]=='(')
				dep++;
			else if(s[i]==')')
			{
				dep--;
				if(dep<0)
				{
					dep++;
					now--;
				}
			}
			else
				now++;
			if(dep==0&&now==1)
			{
				dep++;
				now--;
			}
		}
		if(abs(dep)==now)
			printf("YES\n");
		else
			printf("NO\n");
	}
}

D. Rorororobot

题目大意

给定 n ( 1 ≤ n ≤ 1 0 9 ) n(1 \leq n \leq 10^9) n(1n109) m ( 1 ≤ m ≤ 2 ⋅ 1 0 5 ) m(1 \leq m \leq 2 \cdot 10^5) m(1m2105) 列的网格图,第 i i i 行的第 1 , 2 , 3 , . . . , a i 1,2,3,...,a_i 1,2,3,...,ai 格为障碍物。

q ( 1 ≤ q ≤ 2 ⋅ 1 0 5 ) q(1 \leq q \leq 2\cdot 10^5) q(1q2105) 次询问,每次询问给定起点坐标 ( x s , y s ) (x_s,y_s) (xs,ys) ,终点坐标 ( x t , y t ) (x_t,y_t) (xt,yt) 和正整数 k ( 1 ≤ k ≤ 1 0 9 ) k(1 \leq k \leq 10^9) k(1k109)

从起点开始,每步可以向任意方向移动 k k k 格,途中不能碰到障碍物或走出网格,问能否从起点走到终点。

题解

由于移动是可反悔的,且障碍物均在下方。因此可以先把起点和终点不断移动到上面,再判断能否从起点走到终点。
x s + = ⌊ n − x s k ⌋ ∗ k x_s+=\lfloor \frac{n-x_s}{k} \rfloor*k xs+=knxsk

x t + = ⌊ n − x t k ⌋ ∗ k x_t+=\lfloor \frac{n-x_t}{k} \rfloor*k xt+=knxtk

当起点和终点均向上移动后,若存在合法方案,则必须满足:

  • x坐标相同,即 x s = x t x_s=x_t xs=xt
  • y坐标差为 k k k 的倍数,即 ( y s − y t ) % k = 0 (y_s-y_t)\%k=0 (ysyt)%k=0
  • 期间不存在障碍物,即 max ⁡ { a y s , a y s + 1 , . . . , a y t } < x s \max\{a_{y_s},a_{y_s+1},...,a_{y_t}\}max{ays,ays+1,...,ayt}<xs ,可以用ST表快速求解。注意可能 y t < y s y_tyt<ys ,需要交换左右端点。

参考代码

#include
using namespace std;
typedef long long ll;

const int MAXN=200200;
const int MAXM=20;
int a[MAXN],dp[MAXN][MAXM];

void ST(int n)
{
	int i,j;
	for(i=1;i<=n;i++)
		dp[i][0]=a[i];
	for(j=1;j<MAXM;j++)
	{
		for(i=1;i+(1<<j)-1<=n;i++)
			dp[i][j]=max(dp[i][j-1],dp[i+(1<<(j-1))][j-1]);
	}
}

int rmq(int l,int r)
{
	if(l>r)
		swap(l,r);
	int k=log(r-l+1)/log(2);
	return max(dp[l][k],dp[r-(1<<k)+1][k]);
}

int main()
{
	int n,m,i,q,sx,sy,tx,ty,k;
	scanf("%d%d",&n,&m);
	for(i=1;i<=m;i++)
		scanf("%d",&a[i]);
	ST(m);
	scanf("%d",&q);
	while(q--)
	{
		scanf("%d%d%d%d%d",&sx,&sy,&tx,&ty,&k);
		sx+=(n-sx)/k*k;
		tx+=(n-tx)/k*k;
		if(sx==tx&&abs(ty-sy)%k==0&&rmq(sy,ty)<sx)
			printf("YES\n");
		else
			printf("NO\n");
	}
}

E. XOR Tree

题目大意

给定 n ( 1 ≤ n ≤ 2 ⋅ 1 0 5 ) n(1 \leq n \leq 2 \cdot 10^5) n(1n2105) 个点的树,节点 i i i 具有权值 a i ( 1 ≤ a i < 2 30 ) a_i(1 \leq a_i < 2^{30}) ai(1ai<230)

定义简单路径的权值为路径上的节点权值的异或和。

每次可以将一个节点修改为任意正整数,求最少需要修改多少次,使得树上不存在权值为 0 0 0 的简单路径。

题解

由于被修改后的权值可以很大,因此假设节点 i i i 的权值修改为 2 i + 30 2^{i+30} 2i+30 ,则任何经过节点 i i i 的路径权值均不可能为 0 0 0

考虑DFS逐个处理每个子树内经过子树根节点 x x x 的简单路径。
设集合 S x S_x Sx 表示 x x x 为根的子树内的每个节点到 x x x 的简单路径权值构成的集合,则有转移式

S x = ⋃ s ∈ S o n x S s ⊕ a x S_x=\bigcup_{s\in Son_x}S_s\oplus a_x Sx=sSonxSsax

以上操作,可以用dsu on tree实现,注意由于存在交换大小集合的操作时,需要添加偏移量 l a z laz laz

枚举 x x x 的子节点 s s s 时,经过节点 x x x ,一端在 s s s 为根的子树内,即 u ∈ T s u\in T_s uTs ,另一端为 x x x 为根的的子树中之前已遍历过的节点之一,即 v ∈ T x v i s t e d v\in T_x^{visted} vTxvisted,则其权值构成的集合为

{ V a l u ⊕ V a l v ∣ V a l u ∈ S s , V a l v ∈ S x v i s t e d } \{Val_u\oplus Val_v | Val_u\in S_s,Val_v\in S_x^{visted}\} {ValuValvValuSs,ValvSxvisted}

以上操作,可以通过枚举小集合中的每个元素,用set快速查找大集合中是否存在相同的元素,记得判断是否存在权值为 0 0 0 的简单路径。

若存在权值为 0 0 0 ,且经过节点 x x x 且两端均在 x x x 的子树内的简单路径,则将 a x a_x ax 修改为 2 i + 30 2^{i+30} 2i+30 ,即可排除所有这样的简单路径。
且所有一端在 x x x 的子树中,另一端在 x x x 的子树外的简单路径均可同时被排除,因此清空 S x S_x Sx ,答案 + 1 +1 +1 即可。。

参考代码

#include
using namespace std;
typedef long long ll;

const int MAXN=200200;
int ans=0;
int a[MAXN],fa[MAXN],laz[MAXN];
vector<int> e[MAXN];
set<int> st[MAXN];

void dfs(int x,int p)
{
	st[fa[x]].insert(a[x]);
	int flag=1;
	for(int i=0;i<e[x].size();i++)
	{
		int son=e[x][i];
		if(son==p)
			continue;
		dfs(son,x);
		if(st[fa[x]]>=st[fa[son]])
		{
			swap(fa[x],fa[son]);
			laz[fa[x]]^=a[x];
			laz[fa[son]]^=a[x];
		}
		for(set<int>::iterator it=st[fa[son]].begin();flag&&it!=st[fa[son]].end();it++)
		{
			if(st[fa[x]].find((*it)^laz[fa[son]]^laz[fa[x]])!=st[fa[x]].end())
				flag=0;
		}
		for(set<int>::iterator it=st[fa[son]].begin();flag&&it!=st[fa[son]].end();it++)
			st[fa[x]].insert((*it)^a[x]^laz[fa[son]]^laz[fa[x]]);
	}
	if(!flag)
	{
		st[fa[x]].clear();
		ans++;
	}
}


int main()
{
	int n,i,x,y;
	scanf("%d",&n);
	for(i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
		fa[i]=i;
	}
	for(i=1;i<n;i++)
	{
		scanf("%d%d",&x,&y);
		e[x].push_back(y);
		e[y].push_back(x);
	}
	dfs(1,0);
	printf("%d\n",ans);
}

F. Multiset of Strings

TBD

题目大意

题解

参考代码


你可能感兴趣的:(----Codeforces,算法,图论,c++)