\(\cal T_1\) 小学生物理题

Description

假设你在做电学实验:给定 \(n+2\) 个节点在数轴上排成一排,编号 \(0,1,\dots,n+1\),节点 \(0\)\(-10^{100}\) 的位置,节点 \(1\)\(0\) 的位置,对于 \(i=1,2,\dots,n-1\),节点 \(i+1\) 在节点 \(i\) 正方向距离 \(s_i\) 的位置,节点 \(n+1\)\(10^{100}\) 的位置。对于 \(i=0,1,\dots,n\),节点 \(i\) 与节点 \(i+1\) 之间有一条编号为 \(i\) 的导线。

已知恰有一条导线断掉了,你需要找到这条导线并将其修复。你当前在原点,可以在数轴上任意移动,移动 \(L\) 单位长度的代价是 \(L\)。若你当前在某个节点 \(i=1,2,\dots,n\),则可以花费 \(d_i\) 的代价检查其与节点 \(0\) 是否连通。修复导线 \(i\) 的代价是 \(f_i\).

求在所有策略中,所需代价之和的最大值(即最坏情况)的最小值是多少?

\(2\leqslant n\leqslant 3000,0\leqslant s_i,d_i,f_i\leqslant 10^9\).

Solution

首先考虑区间 \(\mathtt{dp}\),记 \(dp(i,j,0/1)\) 表示断掉的导线在区间 \([i,j]\)(指节点 \(i,j\) 之间的导线),当前在位置 \(i/j\) 的答案。那么有

\[dp(i,j,0)=\min_{k=i+1}^{j-1}\{\max\{dp(i,k,1),dp(k,j,0)\}+s_k+d_k\}-s_i\\dp(i,j,1)=\min_{k=i+1}^{j-1}\{\max\{dp(i,k,1),dp(k,j,0)\}-s_k+d_k\}+s_j \]

其中 \(s_i\) 表示点 \(i\) 到点 \(1\) 的距离。答案就是 \(dp(0,n+1,0)\).

当时写到这里我就寄了,猜了一个决策单调性但是没猜对 /kk.

事实上,这个 \(\mathtt{dp}\) 数组有一个很好的性质 —— \(dp(S,0)\geqslant dp(T,0),T\subseteq S\)\(1\) 的情况同理。

我们转回之前的 \(\mathtt{dp}\) 转移(这里以第一个转移为例),发现其瓶颈在于中间那个 "\(\max\)",不过依据性质,有 \(dp(i,k,1)\leqslant dp(i,k',1),dp(k,j,0)\geqslant dp(k',j,0),k\leqslant k'\),于是这两个函数一个不增,一个不降,如果它们画出的图象存在交点,不妨设 \(p\) 为小于等于这个交点的 \(x\) 坐标的 最大的整数,那么有 \(k\leqslant p\) 时,由 \(dp(k,j,0)+s_k+d_k\) 进行贡献;反之,由 \(dp(i,k,1)+s_k+d_k\) 进行贡献。

到此,我们成功地将取 \(\max\) 转化成了查询满足 \(k\leqslant p(i,j)\),且右端点为 \(j\)\(dp(k,j,0)+s_k+d_k\)\(\min\),和满足 \(k>p(i,j)\),且左端点为 \(i\)\(dp(i,k,1)+s_k+d_k\)\(\min\).

不妨从小到大枚举 \(j\),从大到小枚举 \(i\) 进行 \(\mathtt{dp}\),那么

  • 由于随着 \(i\) 的减小,\(p(i,j)\) 也在减小(这是因为对于同一个 \(k\)\(dp(k,j,0)\) 虽然不变,但是 \(dp(i,k,1)\) 随着 \(i\) 减小而增大,对应在函数图象上就是 \(y\) 值处处不小于之前的 \(y\) 值,交点自然就往左移了),所以之前合法的 \(dp(k,j,0)+s_k+d_k\) 可能不再合法。于是对每个 \(j\) 都维护一个单调队列,不过由于处理过的 \(j\) 不会再被处理,所以也可以直接将用过的队列清空;
  • 随着 \(j\) 的增大,\(p(i,j)\) 也在增大。剩下的内容就是复读上一个讨论了,对每个 \(i\) 都维护一个单调队列即可。

Code

/*
 *  这回只花了 1145141919810 min 就打完了。
 *  真好。记得多手造几组。Codeforces Gym 题拍什么拍?
 */

# include <cstdio>
# include <cctype>
# define print(x,y) write(x), putchar(y)

template <class T>
inline T read(const T sample) {
	T x=0; char s; bool f=0;
	while(!isdigit(s=getchar())) f|=(s=='-');
	for(; isdigit(s); s=getchar()) x=(x<<1)+(x<<3)+(s^48);
	return f? -x: x;
}
template <class T>
inline void write(T x) {
	static int writ[50], w_tp=0;
	if(x<0) putchar('-'), x=-x;
	do writ[++w_tp]=x-x/10*10, x/=10; while(x);
	while(putchar(writ[w_tp--]^48), w_tp);
}

# include <iostream>
using namespace std;
typedef long long ll;

const int maxn = 3005;
const ll infty = 1e17;

int n;
ll dp[maxn][maxn][2];
ll s[maxn], d[maxn], f[maxn];

struct _queue {
	int q[maxn], hd, tl; ll v[maxn];
	void clear() { hd=1, tl=0; }
	void ins(int p, const ll& val) {
		while(hd<=tl && v[tl]>=val) --tl;
		q[++tl]=p, v[tl]=val;
	}
	ll calc(int p) {
		while(hd<=tl && q[hd]<p) ++hd;
		return hd<=tl? v[hd]: infty;
	}
} A[maxn], B[maxn]; 
struct __queue {
	int q[maxn], hd, tl; ll v[maxn];
	void clear() { hd=1, tl=0; }
	void ins(int p, const ll& val) {
		while(hd<=tl && v[tl]>=val) --tl;
		q[++tl]=p, v[tl]=val;
	}
	ll calc(int p) {
		while(hd<=tl && q[hd]>p) ++hd;
		return hd<=tl? v[hd]: infty;
	}
} C, D;

void update(int i,int j) {
	A[i].ins(j, dp[i][j][1]+s[j]+d[j]);
	B[i].ins(j, dp[i][j][1]-s[j]+d[j]);
	C.ins(i, dp[i][j][0]+s[i]+d[i]);
	D.ins(i, dp[i][j][0]-s[i]+d[i]);
}

signed main() {
	freopen("physics.in","r",stdin);
	freopen("physics.out","w",stdout);
	n=read(9); 
	for(int i=2;i<=n;++i) s[i] = s[i-1]+read(9);
	for(int i=1;i<=n;++i) d[i]=read(9);
	for(int i=0;i<=n;++i) 
		f[i]=read(9),
		A[i].clear(), B[i].clear();
	for(int j=1;j<=n+1;++j) {
		dp[j-1][j][0] = dp[j-1][j][1] = f[j-1];
		C.clear(), D.clear(), update(j-1,j); int p = j-1;
		for(int i=j-2;i>=0;--i) {
			while(p>i && dp[p][j][0]<dp[i][p][1]) --p;
			dp[i][j][0] = min(C.calc(p), A[i].calc(p+1))-s[i];
			dp[i][j][1] = min(D.calc(p), B[i].calc(p+1))+s[j];
			update(i,j);
		}
	} print(dp[0][n+1][0],'\n');
	return 0;
}

\(\cal T_2\)

Description

Solution

Code


\(\cal T_3\) 中学生物理题

Description

你需要在平面上放 \(n\) 块双面镜子,它们均与坐标轴成 \(45°\) 角摆放。它们的位置和摆放方向给定,由点坐标 \((x_i,y_i)\) 和字符 /\ 描述。

从沿坐标轴平行的方向射出一束激光,激光经过镜子时会顺/逆时针旋转 \(90°\),最终可能会陷入循环或无限地向某个方向射出。

你需要放 \(4\) 种类型的镜子,要求对于每个可能的循环,一次循环内经过每种类型的镜子的次数相同且为偶数。请构造一种方案,或者证明不存在这种方案。

\(n\leqslant 10^5\).

Solution

神秘建图题。

考虑一面镜子能参与的循环至多只有两个,所以总循环数量是 \(\mathcal O(n)\) 级别的。首先考虑如何找出循环。将每面镜子离散化成两个点,分别表示镜子的两面,再进行最近镜子的连边:具体地,对于 / 的镜子的左部点,它需要找到在此镜子上面/左边最近的镜子,再与其对应离散化点连边。对于没有镜子的情况,建一个虚点连边(注意全局只有 一个虚点)。

如何对镜子染色?把镜子看作一条边,它将镜子拆成的点所属连通块连接起来;同时将连通块看作点。分析一下这张图的构造,实际上就是环与环(可以发现,环也可以和自己连边)、环与虚点、虚点与自己有边。可以证明,所有点的度数均为 \(8\) 的倍数是存在方案的充分必要条件。因为必要性比较显,这里只证明充分性,其实这也是方案的构造方法:考虑利用欧拉回路,由于经过每条边仅一次,那么对于点 \(u\),遍历时每一条入边后都 紧跟着 一条出边,于是可以将图交替染色,就可以将每个点所连的边划分成两个 大小相等 的边集。先分一次,在同色的边集中再分一次即可。

Code

Believe in yourself.
posted on 2022-06-18 10:10  Oxide  阅读(55)  评论(0编辑  收藏  举报