CF_GLOBAL_21 赛时题解
A
z单调减, 直接拿z和每个ai或,找最大
B
w比区间内最小值小,除非区间最小是0;
任何区间变换最多两次就成为0.
拿0当分隔符,若只有一段连续的非0区间,则答案为1
若区间多于1个,那么直接对全局两次操作,答案为2
若全是0,答案为0
C
自己没想出来,看别人说:
把两个都拆到不能再拆,比较是否相同
D
从1往n跳
当前位置为now,now能跳必须满足区间内最大或最小
最大:右端点在向右第一个比它大的big前面,而右端点必须是区间最小,所以在 \((now,big)\) 搜最小值,跳到此处;
最小:右端点在向右第一个比它小的low前面,而右端点必须是区间最大,所以在 \((now,low)\) 搜最大值,跳到此处。
但具体实现不能找到big和low,我们要使用倍增法
对于每一个起始点 t ,它要么是区间内最大,要么是最小。
用倍增法将 \([t+1,n]\) 分割成2的幂,
1)t最大时,区间内最大值小于t , 且区间内最小值能被 t 跳到。这时候刷新 t ,相当于2的幂相加拓展了区间,在区间未搜到的合法部分继续找更小值,直到区间右端点为n;
2)t最小时,同理反过来。
感谢CF leukocyte 的代码:
#include<bits/stdc++.h>
using namespace std;
const int N=2.5e5;
int n;
int p[N+5],rp[N+5];
int mx[20][N+5],mn[20][N+5];
int find(int tp){
int vmn=n+1,vmx=0,t=tp+1;
for(int i=20;i>=0;i--){
if(t+(1<<i)-1>n) continue;
if(mx[i][t]<p[tp]){
vmn=min(vmn,mn[i][t]);
t+=1<<i;
}
}
t=tp+1;
for(int i=20;i>=0;i--){
if(t+(1<<i)-1>n) continue;
if(mn[i][t]>p[tp]){
vmx=max(vmx,mx[i][t]);
t+=1<<i;
}
}
return max(rp[vmn],rp[vmx]);
}
void solve(){
scanf("%d",&n);
rp[0]=rp[n+1]=0;
for(int i=1;i<=n;i++){
scanf("%d",&p[i]); rp[p[i]]=i;
mx[0][i]=p[i]; mn[0][i]=p[i];
}
for(int i=1;n>>i;i++)
for(int j=1;j+(1<<i)-1<=n;j++){
mx[i][j]=max(mx[i-1][j],mx[i-1][j+(1<<i-1)]);
mn[i][j]=min(mn[i-1][j],mn[i-1][j+(1<<i-1)]);
}
int ans=0,tp=1;
while(tp<n){
tp=find(tp); ans++;
}
printf("%d\n",ans);
}
int main(){
freopen("data.in","r",stdin);
int t; scanf("%d",&t);
while(t--) solve();
return 0;
}
E
\(f[i,j]\) 表示向 \((i,j)\) 放一个关键点最少操作多少次能把关键点移出白格。转移形如 \(f[i,j]=f[i+1,j]+f[i,j+1]+1\)
建立一个源点 \(s\),向每个白点连边权为 \(1\) 的边,如果 \((i+1,j)\)是白点 , 连边 \((i+1,j)\to (i,j)\),\((i,j+1)\) 是白点,连边 \((i,j+1)\to (i,j)\)那么 \(f[i,j]\) 等价于 \(s\) 到 \((i,j)\) 所有路径的权值和。这里一条路径的权值定义为路径上所有边权的乘积。那么统计的即 \((0,0)\) 每次向右或向下走,不走出白格子的方案数。
\(\sum_{i=0}^n\sum_{j=0}^{a_i-1}\binom{i+j}{j}=\sum_{i=0}^n\binom{i+a_i}{a_i}\)