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\) 到根的期望路径长度。

\[f_u=\begin{cases}p_uf_u+\frac{1-p_u}{d_u}\sum_{(u,v)\in E}f_v+1 & u\not=1 \\ 0 & u=1\end{cases} \]

可以 \(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_{u,k}=\begin{cases}\sum_{j=0}^{k}\binom{k}{j}(p_uf_{u,j}+\frac{1-p_u}{d_u}\sum_{(y,v)\in E}f_{v,j})&u\not=1\\0&u=1\end{cases} \]

\(f\)\(j\) 分层,时间复杂度 \(O(nk^2)\)

考虑使用

\[x^k=\sum_{j=0}^{k}{k\brace j}\binom{x}{j}j! \]

于是乎,设 \(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)\)

posted @ 2023-11-10 15:46  SError  阅读(14)  评论(0编辑  收藏  举报