首页 > 其他分享 >CSP-S 2022 T4 题解

CSP-S 2022 T4 题解

时间:2022-11-01 09:23:29浏览次数:146  
标签:infty return matrix int 题解 T4 inf CSP dp

简述题意

给一颗 \(n\) 个点的树,每个点有点权 \(v_i\)。有 \(q\) 次询问,每次给出 \((u,v)\),从 \(u\) 开始,每步只能走不超过 \(k\) 条边,走一步的代价是终点的点权,\(v_u\) 也要算在移动的代价中,问 \(u\rightarrow v\) 的最小代价。

数据范围:\(1\le n,q\le 2\times 10^5,1\le v_i\le 10^9,1\le k\le 3\)。

算法分析

考虑 dp,用 \(d_{i,j}\) 表示 \(i,j\) 在树上的距离,\(f_{i,j}\) 表示满足最后一步的终点为 \(\forall x\in V,d_{x,v}\le d_{i,v}\and d_{x,i}=j\) 的方案,就可以得出 \(O(nkq)\) 的暴力。

但发现直接在链上 dp 连样例都过不了,因为 \(x\) 不一定是链上的点,比如下图中绿边表示询问的链,红边可能才是最优的转移:

分类讨论一下不难证明,只有 \(k=3\) 时最优解才有可能跳到路径之外的点上,对 \(k\) 分类,我们可以写出 dp 方程:

  • \(k=1\)

\(f_{i,0}=f_{i-1,0}+v_i\)。(这里进行了重编号,我们认为询问的链上的点的编号是连续的。)

  • \(k=2\)

\[\begin{cases} f_{i,0}=\min\{f_{i-1,0}+a_i,f_{i-1,1}+v_i\}\\ f_{i,1}=f_{i-1,0} \end{cases} \]

  • \(k=3\)

\[\begin{cases} f_{i,0}=\min\{f_{i-1,0}+a_i,f_{i-1,1}+a_i,f_{i-1,2}+v_i\}\\ f_{i,1}=\min\{f_{i-1,0},f_{i-1,1}+w_i\}\\ f_{i,2}=f_{i-1,1} \end{cases} \]

注意到 \(f_{i-1,1}\) 已经包含了 \(f_{i-2,0}\) 的情况,无需更多的分类讨论。

直接转移就是 \(O(nqk)\) 的。注意到 dp 中 \(f_i\) 的取值只和 \(f_{i-1}\) 处的取值有关,可以用矩阵优化。\(\min\) 运算并不是矩阵中应该有的算符,所以我们需要定义新的矩阵乘法:

\[C=A·B\\ C_{i,j}=\min\{A_{i,k}+B_{k,j}\} \]

因为 \(+\) 关于 \(\min\) 有分配律,所以 \((\min,+)\) 的矩阵乘法依然满足结合律。

对于不同的 \(k\) 分别构造转移矩阵,记点 \(i\) 对应的转移矩阵为 \(M_i\)。

  • \(k=1\)

\[M_i=\left[\begin{matrix}v_i\end{matrix}\right] \]

  • \(k=2\)

\[M_i=\left[ \begin{matrix} v_i & 0\\ v_i & +\infty \end{matrix} \right] \]

  • \(k=3\)

\[M_i=\left[ \begin{matrix} v_i & 0 & +\infty\\ v_i & w_i & 0\\ v_i & +\infty & +\infty\\ \end{matrix} \right] \]

那么我们的 dp 过程可以变为:

\[F_{i-1}M_i=F_i \]

一开始有:

\[F_1=\left[\begin{matrix}f_{1,0}\\f_{1,1}\\f_{1,2}\end{matrix}\right]=\left[\begin{matrix}v_1\\+\infty\\+\infty\end{matrix}\right] \]

用树链剖分和线段树维护每条重链上自下而上和自上而下的 \(M_i\) 的乘积转移即可。

代码实现

为了方便,我的代码中把三种矩阵都填充为了 \(3\times3\) 的矩阵,在 \((\min,+)\) 的意义下,我们只需要用 \(+\infty\) 填充空位就可以了,此时单位矩阵为 \(\left[\begin{matrix}0 & +\infty & +\infty\\+\infty & 0 & +\infty\\+\infty & +\infty & 0\end{matrix}\right]\)。

出于个人习惯,在代码中采用的转移方式是 \(M_{i}F_{i-1}=F_i\),这只需要转置上文中出现的矩阵即可。

如果从 \(F_1\) 开始转移的话不能乘 \(M_1\),需要细节处理。不过从 \(F_0=\left[\begin{matrix}0\\+\infty\\+\infty\end{matrix}\right]\) 开始转移就没有这个必要了。

const ll inf=1e18;
namespace sol
{
	const int N=2e5+5,M=2e5+5;
	int n,Q,k;
	ll v[N];
	int fa[N],top[N],son[N],siz[N],dep[N],dfn[N],dfn_cnt,rdfn[N];
	vector<int> e[N];
	ll f[N][3];
	ll ex[N];
	class dp
	{
	public:
		ll a[3][3];
		inline ll *operator[](int i) { return a[i]; }
		inline const ll *operator[](int i) const { return a[i]; }
		dp()
		{
			a[0][0]=0,a[0][1]=inf,a[0][2]=inf;
			a[1][0]=inf,a[1][1]=0,a[1][2]=inf;
			a[2][0]=inf,a[2][1]=inf,a[2][2]=0;
		} dp(ll x) { for(int i=0;i<3;++i) for(int j=0;j<3;++j) a[i][j]=x; }
		dp(ll x00,ll x01,ll x02,ll x10,ll x11,ll x12,ll x20,ll x21,ll x22)
		{
			a[0][0]=x00,a[0][1]=x01,a[0][2]=x02;
			a[1][0]=x10,a[1][1]=x11,a[1][2]=x12;
			a[2][0]=x20,a[2][1]=x21,a[2][2]=x22;
		}
		friend dp operator * (dp a,dp b)
		{
			dp c(inf);
			for(int i=0;i<3;++i)
				for(int j=0;j<3;++j)
					for(int k=0;k<3;++k)
						c[i][j]=min(c[i][j],a[i][k]+b[k][j]);
			return c;
		}
	};
	class node
	{
	public:
		int c[2];
		dp up,down;
		node()=default;
	}; node s[N<<2]; int cnt,rt;
#define ls(x) s[x].c[0]
#define rs(x) s[x].c[1]
#define all rt,1,n
#define mid ((l+r)>>1)
#define L(x) ls(x),l,mid
#define R(x) rs(x),mid+1,r
	inline void pushup(int x)
	{
		if(ls(x))
			s[x].up=s[rs(x)].up*s[ls(x)].up,
			s[x].down=s[ls(x)].down*s[rs(x)].down;
	}
	inline void build(int &x,int l,int r)
	{
		if(!x) x=++cnt;
		if(l==r)
		{
			int u=rdfn[l];
			if(k==1) s[x].up=s[x].down=dp(v[u],inf,inf,inf,inf,inf,inf,inf,inf);
			if(k==2) s[x].up=s[x].down=dp(v[u],v[u],inf,0,inf,inf,inf,inf,inf);
			if(k==3) s[x].up=s[x].down=dp(v[u],v[u],v[u],0,ex[u],inf,inf,0,inf);
			return;
		} build(L(x)),build(R(x)),pushup(x);
	}
	inline dp upquery(int x,int l,int r,int ql,int qr)
	{
		if(l==ql&&r==qr) return s[x].up;
		if(qr<=mid) return upquery(L(x),ql,qr);
		if(ql> mid) return upquery(R(x),ql,qr);
		return upquery(R(x),mid+1,qr)*upquery(L(x),ql,mid);
	}
	inline dp downquery(int x,int l,int r,int ql,int qr)
	{
		if(l==ql&&r==qr) return s[x].down;
		if(qr<=mid) return downquery(L(x),ql,qr);
		if(ql> mid) return downquery(R(x),ql,qr);
		return downquery(L(x),ql,mid)*downquery(R(x),mid+1,qr);
	}
	inline dp up(int x,int y)
	{
		if(dfn[x]<dfn[y]) return upquery(all,dfn[x],dfn[y]);
		return upquery(all,dfn[y],dfn[x]);
	}
	inline dp down(int x,int y)
	{
		if(dfn[x]<dfn[y]) return downquery(all,dfn[x],dfn[y]);
		return downquery(all,dfn[y],dfn[x]);
	}
	inline dp upline(int x,int f)
	{
		dp b;
		while(top[x]!=top[f])
		{
			b=b*up(x,top[x]);
		 	x=fa[top[x]];
		}
		return b*up(x,f);
	}
	inline dp downline(int x,int f)
	{
		static vector<int> tmp;
		if(!tmp.empty()) tmp.clear();
		dp b;
		while(top[x]!=top[f])
		{
			tmp.emplace_back(x);
			x=fa[top[x]];
		} if(x!=f) b=b*downquery(all,dfn[f]+1,dfn[x]);
		reverse(tmp.begin(),tmp.end());
		for(auto x:tmp) b=b*down(x,top[x]);
		return b;
	} inline int lca(int x,int y);
	inline ll line(int x,int y)
	{
		if(x==y) return v[x];
		int l=lca(x,y);
		if(x==l) swap(x,y);
		dp tmp=upline(y,l)*downline(fa[x],l);
		return tmp[0][0]+v[x];
	}
	inline void init()
	{
		f[0][0]=f[0][1]=f[0][2]=inf;
		for(int i=1;i<=n;++i)
		{
			ex[i]=inf;
			for(int to:e[i])
				ex[i]=min(ex[i],v[to]);
		} build(all);
	}
	inline void dfs1(int u,int f)
	{
		fa[u]=f,dep[u]=dep[f]+1,siz[u]=1;
		for(int v:e[u])
			if(v!=f)
		{
			dfs1(v,u); siz[u]+=siz[v];
			if(siz[v]>=siz[son[u]]) son[u]=v;
		}
	}
	inline void dfs2(int u,int tp)
	{
		top[u]=tp,dfn[u]=++dfn_cnt,rdfn[dfn_cnt]=u;
		if(son[u]) dfs2(son[u],tp);
		for(int v:e[u])
			if(v!=fa[u]&&v!=son[u])
				dfs2(v,v);
	}
	inline int lca(int x,int y)
	{
		while(top[x]!=top[y])
		{
			if(dep[top[x]]<dep[top[y]]) swap(x,y);
			x=fa[top[x]];
		} return dep[x]<dep[y]?x:y;
	}
	inline void solmain()
	{
		read_(n),read_(Q),read_(k);
		for(int i=1;i<=n;++i) read_(v[i]);
		for(int i=1,u,v;i<n;++i)
			read_(u),read_(v),
			e[u].emplace_back(v),
			e[v].emplace_back(u);
		dfs1(1,0);
		dfs2(1,1);
		init();
		ll as;
		for(int i=1,x,y;i<=Q;++i)
		{
			read_(x),read_(y);
			as=line(x,y);
			cout<<as<<'\n';
		}
	}
}

个人认为这题做 CSP-S T4 有点简单了,比较套路。

标签:infty,return,matrix,int,题解,T4,inf,CSP,dp
From: https://www.cnblogs.com/efX-bk/p/csp-s-2022-T4-tj.html

相关文章

  • 第三十三章 使用 CSP 进行基于标签的开发 - 使用Hyperevents #server和#call调用服务
    第三十三章使用CSP进行基于标签的开发-使用Hyperevents#server和#call调用服务器端方法使用Hyperevents#server和#call调用服务器端方法hyperevent是我们对web浏......
  • CSP2022 J&S 游记
    终于是“游记”而不是“游寄”了!前言因为没有AK过CSP-J,也没有AK过任何任何CCF的比赛。为了弥补这一遗憾,我报名了今年的CSP-J。但是现在看来,这一举动好像有点多......
  • CSP2022
    CSP取消了,只能vp一下。T1先枚举每个点,bfs出中转次数不超过\(k\)次的点,并标记\(f[i][j]=f[j][i]=1\)。发现\(1\)与\(A,B,C,D\)构成的环可以拆成\(1,A,B\)与......
  • SP9340 题解
    前言题目:三倍经验。SP9340、UVA346、P1931。更好的阅读体验?简单的Floyd。思路......
  • SP19568 题解
    前言题目传送门!更好的阅读体验?好的线段树练习题。思路我们要维护三个操作:单点加。区间推平。区间查询质数。区间推平可以想到珂朵莉树,但是我不会,于是考虑线段树......
  • CSP-S 2022 游记
    9.18初赛内心毫无波澜,这进复赛不是随随便便。下午先来一中,发现yb尾随一女学生......雅礼书院学校好大啊,看着比CSSYZ气派多了。10.9终于考完月考了,化学\(60pts\)......
  • CSP-S 2022 SC 迷惑行为大赏
    【小丑竟是我自己250+->200-】前来报道。本人第一次写,可能不全,可在下面补充。p.s.安利我的博客园。p.p.s.感谢\(\sqrt{\texttt{second}}\)和胖老八等人对本页......
  • CSP-J2 2022 游记
    写在前面的终于进复赛考场了呜呜呜……初赛背得太少,3年没过(应该全谷就我了吧),所以今年是第一年参加复赛。感谢CCF给了我参加复赛的机会。感谢杭师大为我们营造了不......
  • CSP-S复赛退役记
    今天CSP-S第二轮,第一次参加,中午还肚子疼,或许有些耗rp?开考前监考老师念那些注意事项,还有官网,于是她成功把noi念成了nci.开考后打完大体框架,就开始扫视四道题。前两题看的......
  • CSP-S 2022游记
    怎么会有人考完几天之后才写总结的啊-Day???快乐的信竞(摆烂)生活!tlryyyds-Day?每天考试和改题ing...-Day0上午摆烂,下午考模拟赛(信心消失赛)试用了考试的键盘有......