BZOJ杂题选记(8.11-8.16)

P4204 奥运物流

这个是所谓贡献后效性的 dp 问题,详见 2009 年徐源盛的论文。

Code
#include <bits/stdc++.h>
using namespace std;
typedef long double ldb;
const int N = 65;
int n, m, s[N];
vector<int> e[N];
ldb k, c[N], mi[N], dp[N][N][N], tmp[N][N];
void chmax(ldb &a, ldb b){ a = max(a, b); }
void dfs(int u, int d){
	for(int i = 0; i <= m; ++i)
		for(int j = 0; j <= d; ++j) dp[u][i][j] = c[u] * mi[j];
	for(int v : e[u]){
		dfs(v, d + 1);
		memset(tmp, 0, sizeof(tmp));
		for(int i = 0; i <= m; ++i){
			for(int j = 0; j <= d; ++j){
				for(int k = 0; k <= m - i; ++k){
					chmax(tmp[i + k][j], dp[u][i][j] + dp[v][k][j + 1]);
					if(k >= 1) chmax(tmp[i + k][j], dp[u][i][j] + dp[v][k - 1][1]);
				}	
			}
		}
		for(int i = 0; i <= m; ++i){
			for(int j = 0; j <= d; ++j)
				dp[u][i][j] = tmp[i][j];
		}
	} 
}
int main(){
	cin.tie(nullptr)->sync_with_stdio(0);
	cin >> n >> m >> k;
	mi[0] = 1;
	for(int i = 1; i <= n; ++i) cin >> s[i], mi[i] = mi[i - 1] * k;
	for(int i = 1; i <= n; ++i) cin >> c[i];
	int cnt = 1; ldb ans = 0;
	--m;
	for(int u = s[1]; u != 1; u = s[u]){
		++cnt; memset(e, 0, sizeof(e));
		if(s[u] == 1) ++m;
		e[1].push_back(u);
		// cout << u << '\n';
		for(int i = 2; i <= n; ++i){
			if(i == u) continue;
			e[s[i]].push_back(i);
		}
//		for(int i = 1; i <= n; ++i){
//			cout << i << ":\n";
//			for(int j : e[i]) cout << j << ' ';
//			cout << '\n';
//		}
		memset(dp, 0, sizeof(dp));
		dfs(1, 0);
		chmax(ans, dp[1][m][0] / (1 - mi[cnt]));
		//cout << dp[1][m][0] << '\n';
		//cout << '\n';
	}
	cout << fixed << setprecision(2) << ans;
	return 0;
}

P4468 折纸

脑筋急转弯题。求对称点是 trivial 的。

Code
#include <bits/stdc++.h>
using namespace std;
typedef long double ldb;
const ldb eps = 1e-6;
struct vec{
	ldb x, y;
	vec operator + (const vec b){ return {x + b.x, y + b.y}; }
	vec operator - (const vec b){ return {x - b.x, y - b.y}; }
	ldb len(){ return sqrt(x * x + y * y); }
}p[10][2];
ldb dot(vec a, vec b){ return a.x * b.x + a.y *b.y; }
ldb cross(vec a, vec b){ return a.x * b.y - a.y * b.x; }
vec sym(vec a, vec b, vec c){
	vec p1 = b - a, p2 = c - a;
	// cout << p1.x << ' ' << p1.y << ' ' << p2.x << ' ' << p2.y << '\n';
	if(cross(p2, p1) >= -eps) return {-1e9, -1e9};
	//cout << dot(p1, p2) / (p1.len() * p2.len()) << '\n';
	ldb theta = acos(max(min(dot(p1, p2) / (p1.len() * p2.len()), (ldb)1.), (ldb)-1.));
	//cout << theta << '\n';
	ldb cost = cos(-2 * theta), sint = sin(-2 * theta);
	vec tmp = {p2.x * cost - p2.y * sint, p2.x * sint + p2.y * cost};
	return a + tmp; 
}
int n, m, ans;
bool in(vec p){ return p.x > eps && p.x < 100 - eps && p.y > eps && p.y < 100 - eps; }
void dfs(int x, vec now){
	if(x == 0) return void(ans += in(now));
	vec tmp = sym(p[x][0], p[x][1], now);
	if(tmp.x == -1e9) return;
	dfs(x - 1, now);
	// cout << now.x << ',' << now.y << ' ' << x << ' ' << tmp.x << ',' << tmp.y << '\n'; 
	dfs(x - 1, tmp);
}
int main(){
	cin.tie(nullptr)->sync_with_stdio(0);
	cin >> n;
	for(int i = 1; i <= n; ++i)
		cin >> p[i][0].x >> p[i][0].y >> p[i][1].x >> p[i][1].y;
	cin >> m;
	for(int i = 1; i <= m; ++i){
		vec x; cin >> x.x >> x.y;
		ans = 0; dfs(n, x);
		cout << ans << '\n';
	}
	return 0;
}

P4469 最优驾车

一个 trick, 当状态中有有理数时,可以考虑乘上分母的最小公倍数变成整数(通分)。精度问题调了两个小时......

Code
#include <bits/stdc++.h>
using namespace std;
typedef long double ldb;
const ldb eps = 1e-9, inf = 1e9;
const int N = 12, K = 2520;
int n, L, hor[N], col[N];
ldb cal(ldb v){ return 80 - 0.03 * v * v; }
ldb f[N][N][K * 20 + 5];
void chmin(ldb &a, ldb b){ a = min(a, b); }
ldb cvt(int t){ return 1. * t * L / K * 60. / 5.; }
int mceil(ldb t){ return (fabs(floor(t) - t) < eps ? floor(t) : ceil(t)); }
int main(){
    cin.tie(nullptr)->sync_with_stdio(0);
    cin >> n >> L;
    for(int i = 1; i <= n; ++i) cin >> col[i];
    for(int i = 1; i <= n; ++i) cin >> hor[i];
    int xs, ys, xt, yt, t1, t2;
    cin >> xs >> ys >> xt >> yt >> t1 >> t2;
    int d = K * 20;
    for(int i = 0; i <= n; ++i){
        for(int j = 0; j <= n; ++j){
            for(int k = 0; k <= d; ++k)
                f[i][j][k] = inf;
        }
    }
    f[xs][ys][0] = 0;
    int dx = (xs <= xt ? 1 : -1), dy = (ys <= yt ? 1 : -1);
    for(int i = xs; i * dx <= xt * dx; i += dx){
        for(int j = ys; j * dy <= yt * dy; j += dy){
            for(int t = 0; t <= d; ++t){
                if(f[i][j][t] == inf) continue;
                int nxti = i + dx, nxtj = j + dy;
                for(int v = 1; v * 5 <= col[j]; v++){
                    if(t + K / v <= d)
                        chmin(f[nxti][j][t + K / v], f[i][j][t] + L / cal(v * 5));
                }
                for(int v = 1; v * 5 <= hor[i]; v++){
                    if(t + K / v <= d)
                        chmin(f[i][nxtj][t + K / v], f[i][j][t] + L / cal(v * 5));
                }
            }
        } 
    }
    int ansx = 0, ansy = 0;
    for(int t = 0; t <= d; ++t){
        ldb tmpt = cvt(t); 
        // if(tmpt <= t2)cout << tmpt << ' ' << f[xt][yt][t] << '\n'; 
        if(tmpt - t1 >= -eps && tmpt - t2 <= eps){
            if(f[xt][yt][t] != inf){
                // cout << tmpt << ' ' << t << ' ' << f[xt][yt][t] << '\n'; 
                if(!ansx || (tmpt == cvt(ansx) && f[xt][yt][t] < f[xt][yt][ansx] - eps)) ansx = t;
                if(f[xt][yt][t] < f[xt][yt][ansy]) ansy = t;
            }   
        }
    }
    if(!ansx) cout << "No" << '\n';
    else cout << fixed << setprecision(2) << mceil(cvt(ansx)) << ' ' << f[xt][yt][ansx] << '\n' << mceil(cvt(ansy)) << ' ' << f[xt][yt][ansy]; 
    return 0;
}


P3480 KAM-Pebbles

Staircase - Nim 的例题。

先复习一下 Nim :\(n\) 堆物品,每堆有 \(a_i\) 个,两个玩家轮流取走任意一堆的任意个物品,但不能不取。结论是,如果= \(a_1 \oplus a_2 \oplus \ldots \oplus a_n \ne 0\) 那么先手必胜,否则先手必败。证明的话 oiwiki 写得挺好的。

然后附上一个 SG 定理的新理解。对于由 \(n\) 个有向图游戏组成的组合游戏,设它们的起点分别为 \(s_1, s_2, \ldots, s_n\),则有定理:当且仅当 \(\operatorname{SG}(s_1) \oplus \operatorname{SG}(s_2) \oplus \ldots \oplus \operatorname{SG}(s_n) \neq 0\) 时,这个游戏是先手必胜的。同时,这是这一个组合游戏的游戏状态 \(x\) 的 SG 值。

首先可以发现把 \(s_i\) 移动到一个 SG 值更大的地方是没有意义的,因为总是在下一步移动会一个 SG 值等于 \(s_i\) 的地方。那么每次移动就只能让 SG 值变小,如果把 SG 值视作石子数量,就变成了 Nim 游戏。

讲回 Staircase-Nim : 有 \(n\) 堆石子,放在高度为 \(0∼n - 1\) 的阶梯上,第 \(i\) 堆有 \(a_i\) 个。每次选择一堆里 \(≥1\) 个石子放到下一级。谁不能动就输。

可以发现一个性质:如果一个人取了偶数堆上的石子移到奇数堆上,那么下一次操作总可以再把这些石子从奇数堆移动到偶数堆上,直到移动到第 0 堆无法操作,那么拿偶数堆的石子对于奇数堆就没有影响。反之,如果一个人将奇数堆的石子拿到偶数堆,对手就直接按照奇数堆 Nim 的必胜策略操作即可,相当于从奇数堆拿走了一些石子。

因此结论就显然了,只需要看奇数堆的 Nim 和是否为 0。

可以发现,本题是对于差分数组进行 Staircase-Nim。

Code
#include <bits/stdc++.h>
using namespace std;
const int N = 1e3 + 5;
int a[N], c[N];
void solve(){
	int n; cin >> n;
	for(int i = 1; i <= n; ++i)
		cin >> a[i], c[i] = a[i] - a[i - 1];
	int ans = 0;
	for(int i = n; i >= 1; i -= 2) ans ^= c[i];
	cout << (ans == 0 ? "NIE" : "TAK") << '\n';
}
int main(){
	cin.tie(nullptr)->sync_with_stdio(0);
	int T; cin >> T;
	while(T--) solve();
	return 0;
}

P2331 最大子矩阵

终于知道为啥评蓝了......

状态:dp[i][j][k]表示前i行选了j个矩阵,第i行的选的集合为k(k使用二进制)。即:当 k = 00 时,第i行没有选;当 k = 01 时,第i行选了第二个;当 k = 10 时,第i行选了第一个;当 k = 11 时,第i行两个都选了。

但是发现这个 k = 11 的状态有问题。第一列和第二列可能分属两个矩形,就不知道怎么转移了(能修但很麻烦)。

正解是 dp[i][j][k],第一列前 i 行,第二列前 j 行,划分了 k 个矩形。转移是 trival 的。

Code
#include <bits/stdc++.h>
using namespace std;
const int N = 1e2 + 5;
int n, m, k, f[N][N][15], a[N][2], g[N][15];
void chmax(int &a, int b){ a = max(a, b); }
int main(){
	cin.tie(nullptr)->sync_with_stdio(0);
	cin >> n >> m >> k;
	for(int i = 1; i <= n; ++i){
		for(int j = 0; j < m; ++j){
			cin >> a[i][j];
		}
	}
	memset(f, -0x3f, sizeof(f));
	memset(g, -0x3f, sizeof(g));
	g[0][0] = 0; f[0][0][0] = 0;
	if(m == 1){
		for(int i = 1; i <= n; ++i){
			for(int j = 0; j <= k; ++j){
				g[i][j] = g[i - 1][j];
				int sum = 0;
				for(int lst = i; lst; --lst){
					sum += a[lst][0];
					if(j != 0) chmax(g[i][j], g[lst - 1][j - 1] + sum);	
				}
			}
		}
		cout << g[n][k] ;
		return 0;
	}
	f[0][0][0] = 0;
	for(int i = 0; i <= n; ++i){
		for(int j = 0; j <= n; ++j){
			for(int t = 0; t <= k; ++t){
				chmax(f[i + 1][j][t], f[i][j][t]), chmax(f[i][j + 1][t], f[i][j][t]);
				int sum = 0;
				for(int nxti = i + 1; nxti <= n; ++nxti){
					sum += a[nxti][0]; chmax(f[nxti][j][t + 1], f[i][j][t] + sum); 
				}
				sum = 0;
				for(int nxtj = j + 1; nxtj <= n; ++nxtj){
					sum += a[nxtj][1]; chmax(f[i][nxtj][t + 1], f[i][j][t] + sum); 
				}
				if(i == j){
					sum = 0;
					for(int nxt = i + 1; nxt <= n; ++nxt){
						sum += a[nxt][0] + a[nxt][1];
						chmax(f[nxt][nxt][t + 1], f[i][j][t] + sum);
					}
				}
			}
		}
	}
	int ans = -1e9;
	for(int i = 0; i <= k; ++i)
		chmax(ans, f[n][n][i]);
	cout << ans << '\n';
	return 0;
}

P2325 王室联邦

构造题。自己做出来的概率为 0...... 注意最后一个连通块因为大小不够只能并入最后的那个块。

Code
#include <bits/stdc++.h>
using namespace std;
const int N = 1e3 + 5;
vector<int> e[N];
int n, B, st[N], tp, bel[N], cnt, rt[N];
void dfs(int u, int fa){
	int cur = tp;
	for(int v : e[u]){
		if(v != fa){
			dfs(v, u);
			if(tp - cur >= B){
				++cnt;
				while(tp > cur) bel[st[tp]] = cnt, tp--;
				rt[cnt] = u;
			}
		}
	}
	st[++tp] = u;
} 
int main(){
	cin.tie(nullptr)->sync_with_stdio(0);
	cin >> n >> B;
	for(int i = 1; i < n; ++i){
		int u, v;
		cin >> u >> v;
		e[u].push_back(v);
		e[v].push_back(u);
	}	
	dfs(1, 0);
	if(!cnt) ++cnt, rt[cnt] = 1;
	while(tp) bel[st[tp]] = cnt, tp--;
	cout << cnt << '\n';
	for(int i = 1; i <= n; ++i) cout << bel[i] << ' ';
	cout << '\n';
	for(int i = 1; i <= cnt; ++i) cout << rt[i] << ' ';
	return 0;
}

P2327 扫雷

已经退化到连黄也做不出来了是吗。实际上题目的条件很强,所以只要固定第一位,后面就都可以确定了。

Code
#include <bits/stdc++.h>
using namespace std;
const int N = 1e4 + 5;
typedef long long ll;
int a[N], b[N], n;
int check(){
	for(int i = 2; i <= n + 1; ++i){
		b[i] = a[i - 1] - b[i - 1] - b[i - 2];
		if(b[i] >= 2) return 0;
		if(i == n + 1 && b[i] != 0) return 0;
	}
	return 1;
}
int main(){
	cin.tie(nullptr)->sync_with_stdio(0);
	cin >> n;
	for(int i = 1; i <= n; ++i) cin >> a[i];
	int ans = 0;
	b[1] = 1; ans += check();
	b[1] = 0; ans += check();
	cout << ans;
	return 0;
}

P2056 捉迷藏

比较有收获的一道题。首先有一个关于树的直径的结论。若点集 \(S\) 的相距最远的两个点(称为 \(S\) 的直径)为 \(u \to v\),那么,\(S \cup \{P\}\) 的直径为 \(u \to v\), \(v \to P\), \(u \to P\) 中的一个,其中 \(P\) 是一个不存在于 \(S\) 中的点。这个还是很符合直觉的,证明也并不难。

接下来的线段树分治就是 trival 的了。可能需要提一下的是这个用 dfn 求 lca 的科技。

Code
#include <bits/stdc++.h>
using namespace std;
const int N = 5e5 + 5;
vector<int> e[N], op[N * 4];
int dfn[N], st[N][20], tsp, n, dep[N];
void dfs(int u, int fa){
	st[(dfn[u] = ++tsp)][0] = fa;
	for(int v : e[u]){
		if(v != fa) dep[v] = dep[u] + 1, dfs(v, u);
	} 
}
void init(){
	for(int j = 1; j <= ceil(log2(n)); ++j){
		for(int i = 1; i + (1 << j) - 1 <= n; ++i){
			int x = st[i][j - 1], y = st[i + (1 << (j - 1))][j - 1];
			st[i][j] = (dfn[x] < dfn[y] ? x : y);
		}
	}
}
int dist(int u, int v){
	if(u == 0 || v == 0) return -1;
	if(u == v) return 0;
	if(dfn[v] < dfn[u]) swap(u, v);
	int d = log2(dfn[v] - dfn[u]), x = st[dfn[u] + 1][d], y = st[dfn[v] - (1 << d) + 1][d];
	int lca = (dfn[x] < dfn[y] ? x : y);
	return dep[u] + dep[v] - 2 * dep[lca];
}
void update(int p, int pl, int pr, int L, int R, int k){
	if(L <= pl && R >= pr) return void(op[p].push_back(k));
	int mid = (pl + pr) >> 1;
	if(L <= mid) update(p << 1, pl, mid, L, R, k);
	if(R > mid) update(p << 1 | 1, mid + 1, pr, L, R, k);
}
int u, v, query[N], sta[N], lst[N];
void solve(int p, int pl, int pr){
	int inu = u, inv = v;
	for(int ver : op[p]){
		if(!u) u = v = ver;
		else{
			int tmpu = u, tmpv = v;
			if(dist(u, ver) > dist(tmpu, tmpv)) tmpu = u, tmpv = ver;
			if(dist(v, ver) > dist(tmpu, tmpv)) tmpu = v, tmpv = ver;
			u = tmpu, v = tmpv;
		}
	}
	if(pl == pr) return query[pl] = dist(u, v), u = inu, v = inv, void(); 
	int mid = (pl + pr) >> 1;
	solve(p << 1, pl, mid), solve(p << 1 | 1, mid + 1, pr);
	u = inu, v = inv; 
}
int main(){
	cin.tie(nullptr)->sync_with_stdio(0);
	cin >> n;
	for(int i = 1; i < n; ++i){
		int u, v;
		cin >> u >> v;
		e[u].push_back(v);
		e[v].push_back(u);
	}
	int q; cin >> q;
	vector<int> tmp; 
	for(int i = 1; i <= q; ++i){
		char op; cin >> op;
		if(op == 'G') query[i] = 1, tmp.push_back(i);
		else{
			int u; cin >> u;
			if(sta[u] == 0){
				sta[u] = 1;
				if(i != 1) update(1, 1, q, max(lst[u], 1), i - 1, u);
			}
			else{
				sta[u] = 0; 
				lst[u] = i;
			} 
		}
	}
	for(int i = 1; i <= n; ++i) 
		if(sta[i] == 0) update(1, 1, q, max(lst[i], 1), q, i); 
	dfs(1, 0);
	init();
	solve(1, 1, q);
	for(int i : tmp)
		cout << query[i] << '\n'; 
	return 0;
}

P3451 ATR-Tourist Attractions

读懂题之后,最短路和状压的基本想法很符合直觉。唯一需要考虑的就是状压的转移顺序,这个稍微想一下便可以想清楚。然后就是压空间了。

Code
#include <bits/stdc++.h>
using namespace std;
typedef pair<int, int> pii;
const int N = 2e4 + 5, K = 22;

int dist[K][N], f[2][K][200005], _sta[1 << 20], n, m, k, fl, lim[K];
vector<pii> e[N];
vector<int> sta[K];
bitset<N> done; 

void dij(int s){
	priority_queue<pii, vector<pii>, greater<pii> > q;
	memset(dist[s], 0x3f, sizeof(dist[s]));
	done.reset();
	dist[s][s] = 0;
	q.emplace(dist[s][s], s);
	while(!q.empty()){
		int d, u;
		tie(d, u) = q.top();
		q.pop();
		if(done[u]) continue;
		done[u] = 1;
		for(pii t : e[u]){
			int v, w; tie(v, w) = t;
			if(dist[s][u] + w < dist[s][v]){
				dist[s][v] = dist[s][u] + w;
				q.emplace(dist[s][v], v);
			}
		}
	}
}
void chmin(int &a, int b){ a = min(a, b); }
int main(){
	cin.tie(nullptr)->sync_with_stdio(0);
	cin >> n >> m >> k;
	
	for(int i = 1; i <= m; ++i){
		int u, v, w;
		cin >> u >> v >> w;
		e[u].emplace_back(v, w);
		e[v].emplace_back(u, w);
	}
	for(int i = 1; i <= k + 1; ++i) dij(i);
	
	if(k == 0) return cout << dist[1][n], 0;
	
	for(int i = 0; i < (1 << k); ++i){
		int siz = __builtin_popcount(i);
		sta[siz].emplace_back(i), _sta[i] = sta[siz].size() - 1;
	}
	
	int g; cin >> g;
	for(int i = 1; i <= g; ++i){
		int u, v;
		cin >> u >> v;
		lim[v] |= (1 << (u - 2));
	}
	
	memset(f[fl], 0x3f, sizeof(f[fl]));
	for(int i = 2; i <= k + 1; ++i){
		if(lim[i] == 0)
			f[0][i][_sta[1 << (i - 2)]] = dist[1][i];
	}
	for(int i = 2; i <= k; ++i){
		fl ^= 1; memset(f[fl], 0x3f, sizeof(f[fl]));
		for(int j = 0; j < sta[i].size(); ++j){
			int t = sta[i][j]; 
			for(int u = 2; u <= k + 1; ++u){
				if((t & (1 << (u - 2))) && ((lim[u] | t) == t)){
					for(int v = 2; v <= k + 1; ++v){
						if(u != v && (t & (1 << (v - 2))))
							chmin(f[fl][u][j], f[fl ^ 1][v][_sta[t ^ (1 << (u - 2))]] + dist[v][u]);
					}
				}
			}
		}
	}
	
	int ans = 1e9;
	for(int i = 2; i <= k + 1; ++i){
		chmin(ans, f[fl][i][_sta[(1 << k) - 1]] + dist[i][n]);
	}
	cout << ans;
	return 0;
}

P3452 BIU-Offices

经典模型:求反图的连通块个数。

有 trival 的线段树优化建图做法,不过要维护联通性的话有一些细节不太一样。比如说父亲不能在一开始就向儿子连边,而应该在最后重新自上而下的遍历整棵线段树,如果发现一个非叶子节点有其他点向他连边,那么这时再向他的儿子连边。不过这个做法我没写可能还有些别的东西比较麻烦。

居然有非常优秀的 \(O(N + M)\) 做法,实际上感觉顺着往下想一下还是挺自然的。具体来说就是建一个链表,一开始将所有点加入,分为许多轮操作。每一轮开一个队列,先随便从链表里挑一个点 \(u\) 加入队列,然后扩展 \(u\) : 遍历链表,如果一个链表中的点 \(v\)\(u\) 在原图上没有边(反图上有边),那么从链表中删去 \(v\),并加入队列中。扩展后将 \(u\) 弹出。一轮操作结束直到队列为空,本轮操作中加入队列的点为一个连通块。链表变为空时,程序结束。

所有点在链表中被重复遍历到且不被删去的次数是 \(O(M)\) 的,所以总的复杂度就是线性的。

Code
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 5;
vector<int> e[N];
int n, m;
unordered_set<int> s;
bitset<N> bk; 
int main(){
	cin.tie(nullptr)->sync_with_stdio(0);
	cin >> n >> m;
	for(int i = 1; i <= m; ++i){
		int u, v;
		cin >> u >> v;
		e[u].push_back(v);
		e[v].push_back(u);
	}
	for(int i = 1; i <= n; ++i) s.insert(i);
	vector<int> ans;
	while(!s.empty()){
		queue<int> q;
		int siz = 0; 
		q.push(*s.begin());
		s.erase(*s.begin());
		while(!q.empty()){
			int u = q.front(); ++siz, q.pop();
			for(int v : e[u]) bk[v] = 1;
			vector<int> tmp; 
			for(int v : s) 
				if(!bk[v]) tmp.push_back(v), q.push(v);
			for(int v : tmp) s.erase(v);
			for(int v : e[u]) bk[v] = 0;
		}
		ans.push_back(siz);
	}
	sort(ans.begin(), ans.end());
	cout << ans.size() << '\n';
	for(int i : ans) cout << i << ' ';
	return 0;
}

P3453 DRZ-Trees

暴力分讨题。注意 \(1\)\(n\) 的 corner-cases 和相邻两个点交换的情况需要特判。一个启发是离散化不一定要去重(前提是相等的两个元素满足一些性质不影响答案),这样可以在查询时方便的排除相邻的两个数的贡献。

Code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 5e4 + 5;
const ll inf = 1e18;
ll x[N], val[N], _a[N], _b[N];
int a[N], b[N], h[N], n;
void chmin(ll &a, ll b){ a = min(a, b); }
ll swp(int i, int j){
	if(i == j) return 0;
	if(i > j) swap(i, j);
	if(i == 1 && j == 2) return -abs(x[3] - x[2]) + abs(x[3] - x[1]);
	if(i == n - 1 && j == n) return -abs(x[n - 1] - x[n - 2]) + abs(x[n] - x[n - 2]);
	if(i + 1 == j) return -abs(x[i] - x[i - 1]) - abs(x[i + 2] - x[i + 1]) + abs(x[i + 2] - x[i]) + abs(x[i + 1] - x[i - 1]);
	ll ret = 0, tmp[8] = {
		-abs(x[i + 1] - x[i]),
		-abs(x[i] - x[i - 1]), 
		-abs(x[j + 1] - x[j]), 
		-abs(x[j] - x[j - 1]), 
		abs(x[i + 1] - x[j]),
		abs(x[j] - x[i - 1]), 
		abs(x[j + 1] - x[i]), 
		abs(x[i] - x[j - 1])};
	for(int x = 0; x < 8; ++x) ret += tmp[x];
	if(i == 1) ret -= tmp[1] + tmp[5];
	if(i == n) ret -= tmp[0] + tmp[4];
	if(j == 1) ret -= tmp[3] + tmp[7];
	if(j == n) ret -= tmp[2] + tmp[6];
	return ret;
}

struct Segment{
	ll tr[N << 2];
	void init(){ memset(tr, 0x3f, sizeof(tr)); }
	void update(int p, int pl, int pr, int k, ll val){
		if(pl == pr) return tr[p] = val, void();
		int mid = (pl + pr) >> 1;
		if(k <= mid) update(p << 1, pl, mid, k, val);
		if(k > mid) update(p << 1 | 1, mid + 1, pr, k, val);
		tr[p] = min(tr[p << 1], tr[p << 1 | 1]);
	}
	ll query(int p, int pl, int pr, int L, int R){
		L = max(1, L), R = min(R, n);
		if(L > R) return inf;
		if(L <= pl && R >= pr) return tr[p];
		int mid = (pl + pr) >> 1; ll ret = inf;
		if(L <= mid) ret = query(p << 1, pl, mid, L, R);
		if(R > mid) chmin(ret, query(p << 1 | 1, mid + 1, pr, L, R));
		return ret;
	}
}tr;
typedef pair<int, ll> pii;
typedef tuple<int, int, int> tpi;
vector<pii> op[N];
vector<tpi> query[N];
ll ans[N];
void prt(){
	for(int i = 2; i <= n - 1; ++i)
		cout << ans[i] << ' ';
	cout << '\n';
}
ll qry(int pos, int l, int r){
	int i = b[pos], j = a[pos];
	if(j < l || i > r) return tr.query(1, 1, n, l, r);
	if(j == l) return tr.query(1, 1, n, l + 1, r);
	if(j < r){
		if(i < l && j > l) return min(tr.query(1, 1, n, l, j - 1), tr.query(1, 1, n, j + 1, r));
		if(i == l && j > l) return min(tr.query(1, 1, n, l + 1, j - 1), tr.query(1, 1, n, j + 1, r));
		if(i > l) return min({tr.query(1, 1, n, l, i - 1), tr.query(1, 1, n, i + 1, j - 1), tr.query(1, 1, n, j + 1, r)});
	}
	if(j == r){
		if(i < l) return tr.query(1, 1, n, l, r - 1);
		if(i == l) return tr.query(1, 1, n, l + 1, r - 1);
		if(i > l) return min(tr.query(1, 1, n, l, i - 1), tr.query(1, 1, n, i + 1, r - 1));
	}
	if(j > r){
		if(i < l) return tr.query(1, 1, n, l, r);
		if(i == l) return tr.query(1, 1, n, l + 1, r);
		if(i > l && i < r) return min(tr.query(1, 1, n, l, i - 1), tr.query(1, 1, n, i + 1, r));
		if(i == r) return tr.query(1, 1, n, l, r - 1); 
	}
}
void solve1(){
	tr.init();
	auto f = [](int i){ return -val[i] + _a[i] + _b[i] - 2 * x[i]; };
	for(int i = 1; i <= n; ++i) 
		op[i].clear(), query[i].clear();
	for(int i = 2; i <= n - 1; ++i)
		op[1].emplace_back(h[i], f(i)), op[b[i]].emplace_back(h[i], inf);
	for(int i = 2; i <= n - 1; ++i){
		if(b[i] > 1)
			query[h[i]].emplace_back(1, b[i] - 1, i);
	}
	for(int i = 1; i <= n; ++i){
		for(auto [pos, val] : op[i])  tr.update(1, 1, n, pos, val);
		for(auto [l, r, x] : query[i]) chmin(ans[x], qry(x, l, r) + f(x));
	}
	// prt();
}
void solve2(){
	tr.init();
	auto f = [](int i){ return _a[i] - _b[i] - val[i] - 2 * x[i]; }; 
	auto g = [](int j){ return _a[j] + _b[j] - val[j]; };
	for(int i = 1; i <= n; ++i) 
		op[i].clear(), query[i].clear();
	for(int i = 2; i <= n - 1; ++i)
		op[1].emplace_back(h[i], g(i)), op[b[i]].emplace_back(h[i], inf);
	for(int i = 2; i <= n - 1; ++i)
		query[h[i]].emplace_back(b[i], a[i], i);
	for(int i = 1; i <= n; ++i){
		for(auto [pos, val] : op[i])  tr.update(1, 1, n, pos, val);
		for(auto [l, r, x] : query[i]) chmin(ans[x], qry(x, l, r) + f(x));
	}
	// prt();
}
void solve3(){
	tr.init();
	auto f = [](int i){ return -_a[i] - _b[i] - val[i] - 2 * x[i]; }; 
	auto g = [](int j){ return _a[j] + _b[j] - val[j] + 2 * x[j]; };
	for(int i = 1; i <= n; ++i) 
		op[i].clear(), query[i].clear();
	for(int i = 2; i <= n - 1; ++i)
		op[1].emplace_back(h[i], g(i)), op[b[i]].emplace_back(h[i], inf);
	for(int i = 2; i <= n - 1; ++i){
		if(a[i] < n) 
			query[h[i]].emplace_back(a[i] + 1, n, i);
	}
	for(int i = 1; i <= n; ++i){
		for(auto [pos, val] : op[i])  tr.update(1, 1, n, pos, val);
		for(auto [l, r, x] : query[i]) chmin(ans[x], qry(x, l, r) + f(x));
	}
	// prt();
}
void solve4(){
	tr.init();
	auto f = [](int i){ return _a[i] + _b[i] - val[i]; };
	auto g = [](int j){ return _a[j] - _b[j] - val[j] - 2 * x[j]; };
		for(int i = 1; i <= n; ++i) 
		op[i].clear(), query[i].clear();
	for(int i = 2; i <= n - 1; ++i)
		op[b[i]].emplace_back(h[i], g(i)), op[a[i] + 1].emplace_back(h[i], inf);
	for(int i = 2; i <= n - 1; ++i){
		if(b[i] > 1) 
			query[h[i]].emplace_back(1, b[i] - 1, i);
	}
	for(int i = 1; i <= n; ++i){
		for(auto [pos, val] : op[i])  tr.update(1, 1, n, pos, val);
		for(auto [l, r, x] : query[i]) chmin(ans[x], qry(x, l, r) + f(x));
	}
	// prt();
}
void solve5(){
	tr.init();
	auto f = [](int i){ return _a[i] - _b[i] - val[i]; };
	for(int i = 1; i <= n; ++i) 
		op[i].clear(), query[i].clear();
	for(int i = 2; i <= n - 1; ++i)
		op[b[i]].emplace_back(h[i], f(i)), op[a[i] + 1].emplace_back(h[i], inf);
	for(int i = 2; i <= n - 1; ++i)
		query[h[i]].emplace_back(b[i], a[i], i);
	for(int i = 1; i <= n; ++i){
		for(auto [pos, val] : op[i])  tr.update(1, 1, n, pos, val);
		for(auto [l, r, x] : query[i]) chmin(ans[x], qry(x, l, r) + f(x));
	}
	// prt();
}
void solve6(){
	tr.init();
	auto f = [](int i){ return -_a[i] - _b[i] - val[i]; };
	auto g = [](int j){ return _a[j] - _b[j] - val[j] + 2 * x[j]; };
	for(int i = 1; i <= n; ++i) 
		op[i].clear(), query[i].clear();
	for(int i = 2; i <= n - 1; ++i)
		op[b[i]].emplace_back(h[i], g(i)), op[a[i] + 1].emplace_back(h[i], inf);
	for(int i = 2; i <= n - 1; ++i){
		if(a[i] < n)
			query[h[i]].emplace_back(a[i] + 1, n, i);
	}
	for(int i = 1; i <= n; ++i){
		for(auto [pos, val] : op[i])  tr.update(1, 1, n, pos, val);
		for(auto [l, r, x] : query[i]) chmin(ans[x], qry(x, l, r) + f(x));
	}
	// prt();
}
void solve7(){
	tr.init();
	auto f = [](int i){ return _a[i] + _b[i] - val[i] + 2 * x[i]; };
	auto g = [](int j){ return -_a[j] - _b[j] - val[j] - 2 * x[j]; };
	for(int i = 1; i <= n; ++i) 
		op[i].clear(), query[i].clear();
	for(int i = 2; i <= n - 1; ++i)
		op[a[i] + 1].emplace_back(h[i], g(i));
	for(int i = 2; i <= n - 1; ++i){
		if(b[i] > 1)
			query[h[i]].emplace_back(1, b[i] - 1, i);
	}
	for(int i = 1; i <= n; ++i){
		for(auto [pos, val] : op[i])  tr.update(1, 1, n, pos, val);
		for(auto [l, r, x] : query[i]) chmin(ans[x], qry(x, l, r) + f(x));
	}
	// prt();
}
void solve8(){
	tr.init();
	auto f = [](int i){ return _a[i] - _b[i] - val[i] + 2 * x[i]; };
	auto g = [](int j){ return -_a[j] - _b[j] - val[j]; };
	for(int i = 1; i <= n; ++i) 
		op[i].clear(), query[i].clear();
	for(int i = 2; i <= n - 1; ++i)
		op[a[i] + 1].emplace_back(h[i], g(i));
	for(int i = 2; i <= n - 1; ++i){
		query[h[i]].emplace_back(b[i], a[i], i);
	}
	for(int i = 1; i <= n; ++i){
		for(auto [pos, val] : op[i])  tr.update(1, 1, n, pos, val);
		for(auto [l, r, x] : query[i]) chmin(ans[x], qry(x, l, r) + f(x));
	}
	// prt();
}
void solve9(){
	tr.init();
	auto f = [](int i){ return -_a[i] - _b[i] - val[i] + 2 * x[i]; };
	for(int i = 1; i <= n; ++i) 
		op[i].clear(), query[i].clear();
	for(int i = 2; i <= n - 1; ++i)
		op[a[i] + 1].emplace_back(h[i], f(i));
	for(int i = 2; i <= n - 1; ++i){
		if(a[i] < n)
			query[h[i]].emplace_back(a[i] + 1, n, i);
	}
	for(int i = 1; i <= n; ++i){
		for(auto [pos, val] : op[i])  tr.update(1, 1, n, pos, val);
		for(auto [l, r, x] : query[i]) chmin(ans[x], qry(x, l, r) + f(x));
	}
	// prt();
}
int main(){
	cin.tie(nullptr)->sync_with_stdio(0);
	cin >> n;
	vector<int> tmp;
	for(int i = 1; i <= n; ++i) cin >> x[i], tmp.push_back(i);
	sort(tmp.begin(), tmp.end(), [](int i, int j){
		return x[i] < x[j];
	});
	for(int i = 0; i < n; ++i) h[tmp[i]] = i + 1;
	for(int i = 2; i <= n - 1; ++i){
		_a[i] = x[i - 1], _b[i] = x[i + 1];
		val[i] = abs(x[i] - x[i - 1]) + abs(x[i + 1] - x[i]);
		a[i] = h[i - 1], b[i] = h[i + 1];
		if(_a[i] < _b[i]) swap(_a[i], _b[i]);
		if(a[i] < b[i]) swap(a[i], b[i]);
		// cout << h[i] << ' ' << a[i] << ' ' << b[i] << '\n';
	}
	solve1(), solve2(), solve3(), solve4(), solve5(), solve6(), solve7(), solve8(), solve9();
	// cout << swp(2, 3) << '\n';
	for(int i : {1, n}){
		for(int j = 1; j <= n; ++j)
			chmin(ans[i], swp(i, j));
	}
	for(int i = 2; i <= n - 1; ++i){
		for(int j : {1, n})
			chmin(ans[i], swp(i, j));
		chmin(ans[i], min(swp(i, i - 1), swp(i, i + 1)));
	}
	ll anstmp = 0;
	for(int i = 2; i <= n; ++i) anstmp += abs(x[i] - x[i - 1]);
	// cout << anstmp << '\n';
	for(int i = 1; i <= n; ++i){
		cout << anstmp + ans[i] << '\n';
	}
	return 0;
}

posted @ 2025-08-16 21:31  Hengsber  阅读(6)  评论(0)    收藏  举报