0%

03801202-AGC022

C

给定序列 $A$ ,通过若干次操作使其变为序列 $B$ 。对于每次操作,选择一个正整数 $k$ ,对于每个数选择将其变为 $a_i\pmod k$ 或不变,这次操作代价 $2^k$ 。总代价为所有操作代价和,球最小总代价

注意这个 $2^k$ 的代价,其意味着从高位到地位贪心

然后对每个 $k$ 建图判断一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
const int N=55;
int n,f[N][N],a[N],b[N];

inline bool check(ll sta){
for(rei i=0;i<N;++i) for(rei j=0;j<N;++j) f[i][j]=i==j ? 1 : 0;
for(rei i=0;i<N;++i) for(rei j=1;j<=i;++j) if(sta & (1ll<<j)) f[i][i%j]=1;
for(rei k=0;k<N;++k) for(rei i=0;i<N;++i) for(rei j=0;j<N;++j) f[i][j]|=(f[i][k] & f[k][j]);
bool ret=true;
for(rei i=1;i<=n;++i) if(!f[ a[i] ][ b[i] ]){ ret=false; break;}
return ret;
}

int main(){
scanf("%d",&n);
for(rei i=1;i<=n;++i) scanf("%d",&a[i]);
for(rei i=1;i<=n;++i) scanf("%d",&b[i]);
ll sta=(1ll<<38)-2;
if(!check(sta)){ return puts("-1"),0;}
for(rei i=37;i;--i){
ll tmp=sta^(1ll<<i);
if(check(tmp)){ sta=tmp;}
}
printf("%lld\n",sta);
getchar(),getchar();
return 0;
}

D

$N$ 个商场,第 $i$ 个在数轴的 $x_i$ ,需要花费连续的 $t_i$ 时间购物。一趟火车在 $0,L$ 处往返,行驶一单位长度用一单位时间,在 $0$ 时于 $0$ 处上车,只有在 $商场,0,L$ 处才可下车,求在每个超市购物后返回 $0$ 处的最少时间

有一个神仙思路:

对于每个 $t_i$ ,先 $\% 2\times L$ ,再把 $2\times L\times n$ 加到答案里

考虑一个点,如果从左边进只需要到达依次端点就看作左括号,从右边进只需要依次达到端点就看作右括号

先默认每个点贡献都是 $2\times L$ ,那么左右括号匹配时则可以减少 $2\times L$

某些点可以看作左/右括号,此时考虑最大化匹配数

有性质:当一个节点开始时固定作为左括号,则后面一定没有固定作为右括号的,所以不会有两个固定的括号匹配。

贪心即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
const int N=3e5+10;
int n,len,x[N],t[N],l[N],r[N],ans;

int main(){
scanf("%d%d",&n,&len);
for(rei i=1;i<=n;++i) scanf("%d",&x[i]);
for(rei i=1;i<=n;++i) scanf("%d",&t[i]);
for(rei i=1;i<=n;++i){
ans+=t[i]/(2*len); t[i]%=2*len;
if(!t[i]){ --ans; continue;}
l[i]=(t[i]<=x[i]*2);
r[i]=(t[i]<=(len-x[i])*2);
}
rei lim=n,L=0,R=0;
ans+=n+1-r[n];
for(rei i=1;i<n;++i){
if(!l[i] && !r[i]) continue;
if(!r[i]){ lim=i; break;}
if(!l[i] && L) --L,--ans;
else if(l[i]) ++L;
}
for(rei i=n-1;i>=lim;--i){
if(!l[i] && !r[i]) continue;
if(!l[i]) break;
if(!r[i] && R) --R,--ans;
else if(r[i]) ++R;
}
ans-=(L+R)>>1;
printf("%lld\n",2ll*ans*len);
getchar(),getchar();
return 0;
}

E

一个奇数长度的 $01$ 串 $s$ ,其中若干位置是符号 $?$ ,每次将 $3$ 个连续的字符替换成着三个数的中位数,有多少种方案将 $?$ 替换成 $0/1$ 后再进行 $\frac{n-1}{2}$ 次操作后字符串为 $1$

首先考虑没有 $?$ 的情况,考虑其是否合法

维护一个栈,其中包括一段连续的 $0$ 和一段连续的 $1$

  • 加入 $0$
    • 栈顶 $0$ :若栈内 $2$ 个 $0$ ,则替换成一个 $0$ ,否则入栈
    • 栈顶 $1$ :直接入栈
  • 加入 $1$
    • 栈顶 $0$ :弹出栈顶的 $0$ ,相当于抵消
    • 栈顶 $1$ :若栈内只有一个 $1$ 则入栈,否则不管

那么最后只要 $num_1>num_0$ 则合法

简化一下栈内部:发现 $0,1$ 的个数只可能是 $0\sim 2$ ,设 $f_{i,a,b}$ 表示第 $i$ 位时,栈内有 $a$ 个 $1$ ,$b$ 个 $0$ 的方案数

有转移:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
const int N=3e5+100;
const int mod=1e9+7;
int n,ans;
int f[N][3][3];
char s[N];

inline void fix(int &x){ x>=mod ? x-=mod : 0;}

int main(){
scanf("%s",s+1); n=strlen(s+1);
f[0][0][0]=1;
for(rei i=0;i<n;++i){
if(s[i+1]!='1')
for(rei a=0;a<=2;++a){
fix(f[i+1][a][1]+=f[i][a][2]);
fix(f[i+1][a][1]+=f[i][a][0]);
fix(f[i+1][a][2]+=f[i][a][1]);
}
if(s[i+1] != '0'){
for(rei a=0;a<=2;++a){
fix(f[i+1][a][0]+=f[i][a][1]);
fix(f[i+1][a][1]+=f[i][a][2]);
}
fix(f[i+1][1][0]+=f[i][0][0]);
fix(f[i+1][2][0]+=f[i][1][0]);
fix(f[i+1][2][0]+=f[i][2][0]);
}
}
for(rei a=0;a<=2;++a) for(rei b=0;b<=a;++b) fix(ans+=f[n][a][b]);
printf("%d\n",ans);
getchar(),getchar();
return 0;
}

F

$x=10^{100}$ ,数轴上有 $n$ 个点,点 $i$ 的坐标为 $x_i$ ,$n-1$ 次操作,每次选择两点 $A,B$ ,将 $A$ 移动到 $B$ 的对称点并删去 $B$ ,求最后剩的数取值的方案数

当 $关于B对称的A$ 变成 $2B-A$ 时,即 $B被消掉$ 时,将 $A$ 视作 $B$ 的父亲,最终形成一颗具有儿子相对顺序的树,如此最终剩下来的点就是根节点

将一个点 $A$ 的贡献视作 $2^{d_A} \times c_A \times x_A$ ,其中 $x_A$ 视作未知数, $d_A$ 为节点 $A$ 深度 ,$c_A$ 为正负号

考虑到 $x_i$ 极大且 $x_{i+1}-x_i$ 也极大,那么不会出现点贡献相互抵消,那么只需要考虑前面的系数即可

深度显然易得,考虑如何求 $c_i$

这里采取间接确定每一项的符号,即,对于点 $i$ 的字节点 $son_1,son_2,…son_k$ ,已知点 $i$ 的符号,观察 $k$ 个子节点符号与 $i$ 的关系

当 $i$ 再向上与 $fa_i$ 连边时,相当于有 $2\times val_i-val_{fa}$ ,而 $son_i$ 又有贡献 $2\times val_{son}-2\times val_i$ ,所以连边后 $i$ 与 $son_i$ 的正负号会同时取反

而当 $son_i$ 连向 $i$ 时,对于 $son_i$ ,其实质相当于 $\times 2$ ,而对于 $i$ ,其实质为 $\times -1$

那么有结论:当且仅当节点 $v$ 有奇数个字节点时,其符号与父节点不一样

考虑 $dp$ 解决:

设 $f_{i,j}$ 表示放了 $i$ 个节点,最后一层有 $j$ 个节点的儿子数量为奇数时的方案数

为了方便将 $c_i$ 差分,与 $fa$ 相同记作 $1$ ,不同记作 $-1$ ,转移时仅关心下一层与 $fa$ 相同/不同的数量

对于点 $v$ 有 $k$ 个儿子,若不考虑儿子的儿子的影响,应该有 $\left\lfloor \frac{k}{2} \right\rfloor$ 个儿子的 $c$ 与 $v$ 不同

设下一层长出 $k$ 个节点,其中 $\frac{k-j}{2}$ 个点正负与父亲不同(此时kj奇偶性不同则跳过)

设考虑儿子的儿子的影响后,点 $v$ 有 $x$ 个儿子与之正负不同,那么就有 $|x-\frac{k-j}{2}|$ 个节点( $son_v$ )的正负性需要修正,即,下一层中的点的儿子个数为奇数的节点数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const int N=55;
const int mod=1e9+7;
ll c[N][N],f[N][N]; int n;

int main(){
scanf("%d",&n);
for(rei i=0;i<=n;++i){
c[i][0]=1;
for(rei j=1;j<=i;++j) c[i][j]=(c[i-1][j-1]+c[i-1][j])%mod;
}
f[1][0]=f[1][1]=n;
for(rei i=1;i<n;++i) for(rei j=0;j<=i;++j) for(rei k=(!j ? 2 : j);i+k<=n;k+=2){
rei t=(k-j)>>1;
for(rei x=0;x<=k;++x) (f[i+k][abs(x-t)]+=f[i][j]*c[n-i][k]%mod*c[k][x]%mod)%=mod;
}
printf("%lld\n",f[n][0]);
getchar(),getchar();
return 0;
}