视频讲解:TBD
有 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");
}
}
有长为 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=prei−1+max(0,ai−1−ai)
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+1−ai)
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={pret−pressuft−sufst≥st<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]);
}
}
给定带未知符’?'的括号序列,每个"?“表示为”(“或”)",求是否存在唯一方案得到合法的括号序列
从左到右遍历,维护以下两个初始为 0 0 0 的变量:
遍历时,存在以下两个特殊情况:
最终判断 ∣ 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");
}
}
给定 n ( 1 ≤ n ≤ 1 0 9 ) n(1 \leq n \leq 10^9) n(1≤n≤109) 行 m ( 1 ≤ m ≤ 2 ⋅ 1 0 5 ) m(1 \leq m \leq 2 \cdot 10^5) m(1≤m≤2⋅105) 列的网格图,第 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(1≤q≤2⋅105) 次询问,每次询问给定起点坐标 ( 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(1≤k≤109) 。
从起点开始,每步可以向任意方向移动 k k k 格,途中不能碰到障碍物或走出网格,问能否从起点走到终点。
由于移动是可反悔的,且障碍物均在下方。因此可以先把起点和终点不断移动到上面,再判断能否从起点走到终点。
x s + = ⌊ n − x s k ⌋ ∗ k x_s+=\lfloor \frac{n-x_s}{k} \rfloor*k xs+=⌊kn−xs⌋∗k
x t + = ⌊ n − x t k ⌋ ∗ k x_t+=\lfloor \frac{n-x_t}{k} \rfloor*k xt+=⌊kn−xt⌋∗k
当起点和终点均向上移动后,若存在合法方案,则必须满足:
#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");
}
}
给定 n ( 1 ≤ n ≤ 2 ⋅ 1 0 5 ) n(1 \leq n \leq 2 \cdot 10^5) n(1≤n≤2⋅105) 个点的树,节点 i i i 具有权值 a i ( 1 ≤ a i < 2 30 ) a_i(1 \leq a_i < 2^{30}) ai(1≤ai<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=s∈Sonx⋃Ss⊕ax
以上操作,可以用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 u∈Ts ,另一端为 x x x 为根的的子树中之前已遍历过的节点之一,即 v ∈ T x v i s t e d v\in T_x^{visted} v∈Txvisted,则其权值构成的集合为
{ 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}\} {Valu⊕Valv∣Valu∈Ss,Valv∈Sxvisted}
以上操作,可以通过枚举小集合中的每个元素,用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);
}
TBD