2023.11.10
A
一棵 \(n\) 个点的树,每个节点有两个权值 \(a,b\),初始均为 \(0\)。
在每个时刻,可以选择至多一个节点 \(x\)(或者不选),操作 \(b_x\leftarrow b_x\oplus 1\)。对于在当前时刻所有 \(b\) 值为 \(1\) 的节点 \(x\),令 \(a_x\leftarrow a_x\oplus 1\),此后,令 \(b_x\leftarrow b_x\oplus 1\),\(b_{fa_x}\leftarrow b_{fa_x}\oplus 1\)(当 \(fa_x\) 存在时)。
给出 \(\{a_n\}\) 的目标状态,问能够达到目标的最小时刻。
\(1\le n\le 16\),保证 \(fa_i<i\)。
我们以时间为第一维做可行性状压 DP。
代码有一些费用最后计算的意味,这使得我们能够节省状态数。
当然可能就是我太唐了。
点击查看代码
#include<bits/stdc++.h>
#define N 20
#define M (1<<16)
using namespace std;
int read(){
int x=0,w=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}
while(isdigit(ch))x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
return x*w;
}
int n,fa[N],S[N][N];
bool f[N][M];int state;
int main(){
n=read();
for(int i=2;i<=n;i++)fa[i]=read();
for(int i=1;i<=n;i++)
for(int j=1,k=i;j<=n;j++,k=fa[k])
S[i][j]=S[i][j-1]^(k?(1<<(k-1)):0);
for(int i=1;i<=n;i++)
if(read())state|=(1<<(i-1));
f[1][0]=true;
int lim=(1<<n);
for(int i=1;i<=n;i++){
if(f[i][state]){
printf("%d\n",i-1);
exit(0);
}
for(int s=0;s<lim;s++){
if(!f[i][s])continue;
f[i+1][s]=true;
for(int j=1;j<=n;j++)
f[i+1][s^S[j][i]]=true;
}
}
return 0;
}
B
有 \(n\) 个点,给出 \(m\) 条信息,每个形如四元组 \((u_j,v_j,L_j,R_j)\)。
对于条件 \(j\),选择一个 \(t\in[L_j,R_j]\),将条件按时间排序之后,若 \(u_j\) 和 \(v_j\) 之一与 \(1\) 连通,将它们均并入 \(1\) 所在的连通块中。如果一个点 \(x\) 在时间 \(t-1\) 及之前已经与 \(1\) 连通,优先进行 \(x\) 的操作。
给出 \(n\) 个数,值域为 \(\{1,0,-1\}\),\(1\) 表示其一定与 \(1\) 连通,\(-1\) 表示一定不与 \(1\) 连通,\(0\) 表示不考虑连通性,问是否存在合法的 \(\{t\}\)。
\(n,m\le 2\times 10^5\),\(u_j\not=v_j\),\(1\le L_i\le R_i\le 10^9\)。
C
一个 01 串,每次取出一个子串作为二进制数 \(x\),可以令 \(x\leftarrow x+2^k\) 或 \(x-2^k\),其中 \(k\in \mathbb{N}\),但需满足 \(x\) 时刻 \(\ge 0\),问使 \(x\) 变为 \(0\) 的最小步数。串支持单点修。
\(n,q\le 3\times 10^5\)。
有一个简单的结论。若两段 \(1\) 的间隔为 \(1\),可以将它们合并减少 \(1\) 步。此外的连续段代价为 \(2\),孤立点代价为 \(1\)。把这个放到线段树上维护,然后写的太屎就睡觉去了。
考虑 \(O(n)\) DP。记 \(f_{i,0/1}\) 表示使得第 \(i\) 位为 \(0\),当前不进位和进位的最小步数。
- \(f_{i,0}=f_{i-1,0}+[s_i=1]\),若 \(s_i=0\),同时对 \(f_{i-1,1}+1\) 取 \(\min\)。
前者是说可以当 \(-2^k\) 使当前位为 \(0\),后者的意思是如果在 \(i-1\) 位做了进位操作,这一位必须为 \(0\)。
- \(f_{i,1}=f_{i-1,1}+[s_i=0]\),若 \(s_i=1\),同时对 \(f_{i-1,0}+1\) 取 \(\min\)。
初始值 \(f_{l-1,0}=0,f_{l-1,1}=\infty\),答案为 \(\min(f_{r,0},f_{r,1}+1)\)。
和前面差不多。
于是乎考虑 \(i\) 处的转移矩阵。时间复杂度 \(O(q\log n)\)。
点击查看代码
#include<bits/stdc++.h>
#define N 300010
#define inf 1e9
using namespace std;
int read(){
int x=0,w=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}
while(isdigit(ch))x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
return x*w;
}
struct Mat{
int a[2][2];
Mat(){a[0][0]=a[0][1]=a[1][0]=a[1][1]=inf;}
Mat operator*(const Mat &b)const{
Mat ret;
for(int i=0;i<2;i++)
for(int k=0;k<2;k++)
for(int j=0;j<2;j++)
ret.a[i][j]=min(ret.a[i][j],a[i][k]+b.a[k][j]);
return ret;
}
}tr[N<<2];
int n;char _s[N];int s[N];
#define ls p<<1
#define rs p<<1|1
void pushup(int p){
tr[p]=tr[ls]*tr[rs];
}
void build(int p,int l,int r){
if(l==r){
tr[p].a[0][0]=s[l],tr[p].a[1][0]=!s[l]?1:inf;
tr[p].a[0][1]=s[l]?1:inf,tr[p].a[1][1]=!s[l];
return;
}
int mid=(l+r)>>1;
build(ls,l,mid),build(rs,mid+1,r);
pushup(p);
}
void modify(int p,int l,int r,int x){
if(l==r){
tr[p].a[0][0]=s[l],tr[p].a[1][0]=!s[l]?1:inf;
tr[p].a[0][1]=s[l]?1:inf,tr[p].a[1][1]=!s[l];
return;
}
int mid=(l+r)>>1;
(x<=mid)?modify(ls,l,mid,x):modify(rs,mid+1,r,x);
pushup(p);
}
Mat ans;
void query(int p,int l,int r,int L,int R){
if(L<=l&&r<=R)return ans=ans*tr[p],void();
int mid=(l+r)>>1;
if(L<=mid)query(ls,l,mid,L,R);
if(R>mid)query(rs,mid+1,r,L,R);
}
int main(){
n=read(),scanf("%s",_s+1);
for(int i=1;i<=n;i++)s[i]=_s[i]-'0';
build(1,1,n);
int m=read(),opt,l,r,x,y;
while(m--){
opt=read();
if(opt==1){
l=read(),r=read();
ans.a[0][0]=0,ans.a[0][1]=inf;
query(1,1,n,l,r);
printf("%d\n",min(ans.a[0][0],ans.a[0][1]+1));
}
if(opt==2){
x=read(),y=read();
s[x]=y,modify(1,1,n,x);
}
}
return 0;
}
D
树上随机游走。具体地,在点 \(u\) 有 \(p_u\) 的概率停留,\(1-p_u\) 的概率向任意相邻节点移动。
求到达 \(1\) 节点的时间的 \(k\) 次方的期望值。答案模 \(998244353\)。
\(n\cdot k\le 10^6\),\(1\le n\le 10^5\),\(0\le k\le 10^5\),\(0\le p_i<1\)。
wzr 暴力求逆草过去了?
先考虑 \(k=1\) 的情况。设 \(f_i\) 为 \(i\) 到根的期望路径长度。
可以 \(O(n^3)\) 暴力高消。
发现叶子节点只存在一个关于 \(f_x\) 和 \(f_{fa_x}\) 的方程。
将一个点的所有儿子消去后,该点可视作叶子节点。
即知道了父亲节点,就可以倒推出其所有儿子节点。
考虑 \(k>1\) 的情况。设 \(f_{i,j}\) 为 \(i\) 到根的所有路径的 \(j\) 次方的期望。
由二项式定理 \(\displaystyle (x+1)^k=\sum_{j=0}^{k}\binom{k}{j}x^j\) 有
将 \(f\) 按 \(j\) 分层,时间复杂度 \(O(nk^2)\)。
考虑使用
于是乎,设 \(f_{u,j}\) 为 \(u\) 到根的所有路径长度 \(t\),\(\binom{t}{j}\) 的期望,由 \(\displaystyle \binom{i}{j}=\binom{i-1}{j}+\binom{i-1}{j-1}\) 可以 \(O(nk)\) 转移。用多项式预处理 \(\forall j\in[0,k],{k\brace j}\) 即可。
时间复杂度 \(O(nk+k\log k)\)。