一.题目大意
给你一个无向图,第i和i+1条边的权值是w[i],问你每个点不在自己原本的点的代价是多少,会有q组询问,表示修改第i条边的权值。
二.解题思路
可以观察到,完成这个操作需要每条边经过两次,那么我们在不考虑修改的情况下进行dp[i]表示考虑前i条边,最后一条边经过i的最小代价。此时状态转移方程可以很显然的观察出来就是dp[i] = min(dp[i - 1],dp[i - 2]) + a[i]。
考虑到修改,我们利用线段树维护dp的过程,考虑一个递推式子,我们可以构造出一个矩阵,右乘一个等于,当我们把乘法定义为+法,求和定义为取min,那么这个矩阵就符合转移方程,因此只需要构造n个A矩阵,就可以把答案递推出来,此时用线段树维护矩阵修改就行。
三.代码实现
#include "bits/stdc++.h"
using namespace std;
using ll = long long;
const int N = 2e5 + 10;
const ll inf = 0x3f3f3f3f3f3f3f3f;
struct Mat {
ll dp[2][2];
Mat operator *(const Mat &T) const {
Mat res;
for(int i = 0;i < 2;i ++) {
for(int j = 0;j < 2;j ++) {
res.dp[i][j] = inf;
for(int k = 0;k < 2;k ++) {
res.dp[i][j] = min(res.dp[i][j],dp[i][k] + T.dp[k][j]);
}
}
}
return res;
}
};
Mat bas[N],val[N << 2];
void push_up(int u)
{
val[u] = val[u << 1] * val[u << 1 | 1];
}
void build(int u,int l,int r)
{
if(l == r) {
val[u] = bas[l];
return;
}
int mid = (l + r) >> 1;
build(u << 1,l,mid);
build(u << 1 | 1,mid + 1,r);
push_up(u);
}
void modify(int u,int l,int r,int p)
{
if(l == r) {
val[u] = bas[p];
return;
}
int mid = (l + r) >> 1;
if(p <= mid) modify(u << 1,l,mid,p);
else modify(u << 1 | 1,mid + 1,r,p);
push_up(u);
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
int n;
cin >> n;
n --;
for(int i = 1,x;i <= n;i ++) {
cin >> x;
bas[i].dp[0][0] = bas[i].dp[0][1] = x;
bas[i].dp[1][1] = inf;
}
build(1,1,n);
int q;
cin >> q;
while(q --) {
int p,v;
cin >> p >> v;
bas[p].dp[0][0] = bas[p].dp[0][1] = v;
bas[p].dp[1][1] = inf;
modify(1,1,n,p);
cout << val[1].dp[0][1] * 2 << '\n';
}
return 0;
}