Gym 题选做

104160G Meet in the Middle

题意:给定两棵 \(n\) 个点(\(n\leq 10^5\))的树,边有边权,\(q\) 次询问(\(q\leq 5\times 10^5\)),每次询问给定 \(a,b\),希望你找到一个点 \(x\),使得 \(dis1_{a,x}+dis2_{b,x}\) 最大,求这个最大值。

key words:直径、线段树维护技巧

考虑向同一棵树上转化。我们给 \(T2\) 的每一个节点上都挂一个附属节点,边权为当前询问下,\(T1\)\(a\) 到每个点的距离。此时我们把问题转化到了 \(T2\) 上,即求 \(T2\)\(a\) 点到某个叶子的路径长度最大值。不难想到这样的叶子一定是直径的端点之一。

不难想到将询问离线并挂在 \(a\) 上。需要解决的问题是随着 \(a\) 的变化,边权及直径会如何变化。你发现当 \(a\) 在树上移动一个位置时,\(dfn\) 序上的某些区间内的距离会增加,某些会减小,相应的是边权会增加或减小。因此,我们不直接维护直径的值,而是只维护直径的端点。对 \(T1\)\(dfn\) 序列开线段树,维护对应区间组成的点集在 \(T2\) 上的直径的端点,合并时枚举端点选择最长的即可。求新 \(T2\) 上距离时,分三部分考虑,两个叶子的边在 \(T1\) 上求到当前 \(a\) 的距离;剩余的在 \(T2\) 上求距离,使用欧拉序和 ST 表可以做到 \(O(1)\)

\(a\) 下移至 \(x\) 时,区间 \([dfn_x,dfn_x+sz_x-1]\) 的点距离会有一个变化;剩余点会有另一个变化。因此,对于线段树上未覆盖 \(dfn_x\)\(dfn_x+sz_x-1\) 位置的节点区间对应的点集中,由于所有边的变化相同,直径的两个端点是不会变化的,因此我们只需要对线段树上 \(dfn_x\)\(dfn_x+sz_x-1\) 的两个位置做单点修改即可。

故总复杂度可以做到 \(O(n\log n+q)\)

#include<bits/stdc++.h>
#define rep(i,j,k) for(int i=j;i<=k;i++)
#define repp(i,j,k) for(int i=j;i>=k;i--)
#define ls(x) (x<<1)
#define rs(x) ((x<<1)|1)
#define mp make_pair
#define sec second
#define fir first
#define pii pair<int,int>
#define lowbit(i) i&-i
using namespace std;
typedef long long ll;
const int N=1e5+5,M=5e5+5,S=(1<<15)+5,inf=1e9+7,mo=1e9+7;
const double eps=1e-8;
void read(int &p){
	int x=0,w=1;
	char ch=0;
	while(!isdigit(ch)){
		if(ch=='-')w=-1;
		ch=getchar();
	}
	while(isdigit(ch)){
		x=(x<<1)+(x<<3)+ch-'0';
		ch=getchar();
	}
	p=x*w;
}
int n,m;
struct tree{
	vector<pii>e[N];
	int dfn[N],cntp,nw[N];
	int fa[N][20],dep[N],sz[N],eular[2*N],cntu,pose[N];
	ll lenf[N],dis[N];
	void dfs(int x,int f){
		fa[x][0]=f,dep[x]=dep[f]+1,sz[x]=1;
		dfn[x]=++cntp,nw[cntp]=x;
		eular[++cntu]=x,pose[x]=cntu;
		rep(i,1,17)
		    fa[x][i]=fa[fa[x][i-1]][i-1];
		for(auto p:e[x]){
			int j=p.fir,w=p.sec;
			if(j==f)continue;
			dis[j]=dis[x]+w,lenf[j]=w;
			dfs(j,x),sz[x]+=sz[j];
			eular[++cntu]=x;
		}
	}
	int lg[2*N],st[2*N][20];
	void init(){
		lg[1]=0;
		rep(i,2,cntu)
		    lg[i]=lg[i/2]+1;
	}
	void build(){
		init();
		rep(i,1,cntu)
		    st[i][0]=dfn[eular[i]];
		rep(j,1,18){
			rep(i,1,cntu){
				st[i][j]=st[i][j-1];
				if(i+(1<<(j-1))<=cntu)st[i][j]=min(st[i][j],st[i+(1<<(j-1))][j-1]);
			}
		}
	}
	int query(int x,int y){
		if(x>y)swap(x,y);
		int len=y-x+1;
		return min(st[x][lg[len]],st[y-(1<<lg[len])+1][lg[len]]);
	}
	int lca(int x,int y){
		return nw[query(pose[x],pose[y])];
	}
	ll getdis(int x,int y){
		return dis[x]+dis[y]-2ll*dis[lca(x,y)];
	}
	void init_tree(){
		rep(i,1,n-1){
			int x,y,w;
			read(x),read(y),read(w);
			e[x].push_back(mp(y,w)),e[y].push_back(mp(x,w));
		}
		dfs(1,0);
		build();
	}
}T1,T2;
vector<pii>q[N];
int nqp=1;
ll getdim(int x,int y){
	return T1.getdis(nqp,x)+T1.getdis(nqp,y)+T2.getdis(x,y);
}
struct seg1{
	int t[4*N][2];
	void pushup(int x){
		ll res=0;
		ll nwv=getdim(t[ls(x)][0],t[ls(x)][1]);
		if(nwv>res)t[x][0]=t[ls(x)][0],t[x][1]=t[ls(x)][1],res=nwv;
		nwv=getdim(t[rs(x)][0],t[rs(x)][1]);
		if(nwv>res)t[x][0]=t[rs(x)][0],t[x][1]=t[rs(x)][1],res=nwv;
		rep(i,0,1){
			rep(j,0,1){
				nwv=getdim(t[ls(x)][i],t[rs(x)][j]);
				if(nwv>res)t[x][0]=t[ls(x)][i],t[x][1]=t[rs(x)][j],res=nwv;
			}
		}
	}
	void build(int x,int le,int ri){
		if(le==ri){
			t[x][0]=t[x][1]=T1.nw[le];
			return;
		}
		int mid=(le+ri)>>1;
		build(ls(x),le,mid),build(rs(x),mid+1,ri);
		pushup(x);
	}
	void modify(int x,int le,int ri,int p){
		if(le==ri)return;
		int mid=(le+ri)>>1;
		if(p<=mid)modify(ls(x),le,mid,p);
		else modify(rs(x),mid+1,ri,p);
		pushup(x);
	}
}SGT1;
void update(int x,int v){
	if(v>0)nqp=x;
	else nqp=T1.fa[x][0];
	SGT1.modify(1,1,n,T1.dfn[x]),SGT1.modify(1,1,n,T1.dfn[x]+T1.sz[x]-1);
}
ll ans[M];
void dfs(int x,int f){
	if(x!=1)update(x,1);
	int l=SGT1.t[1][0],r=SGT1.t[1][1];
	for(auto j:q[x]){
	    ll valx=T1.getdis(l,nqp)+T2.getdis(l,j.fir);
	    ll valy=T1.getdis(r,nqp)+T2.getdis(r,j.fir);
	    ans[j.sec]=max(valx,valy);
	}
	for(auto j:T1.e[x]){
		if(j.fir==f)continue;
		dfs(j.fir,x);
	}
	if(x!=1)update(x,-1);
}
signed main(){
	read(n),read(m);
	T1.init_tree(),T2.init_tree();
	rep(i,1,m){
		int x,y;
		read(x),read(y);
		q[x].push_back(mp(y,i));
	}
	SGT1.build(1,1,n);
	dfs(1,0);
	rep(i,1,m)
	    printf("%lld\n",ans[i]);
	return 0;
}

102586B Evacuation

题意:有一行 \(n\) 个城镇,每个城镇有可以容纳 \(a_i\) 人的庇护所。现有 \(s\) 个人在这些城镇中。\(q\) 次询问,每次给定 \(l,r\),表示陨石将会袭击 \([l,r]\) 内的城镇,人们要么在庇护所中,要么不在 \([l,r]\) 的城镇内。将一个人移动到相邻城镇的花费为 \(1\),求所有人员分布情况下,让所有人保持安全的最小花费的最大值。\(n,q\leq 2\times 10^5\)

key words:离线、线段树分治、决策单调性

首先有一个显然的观察是:花费最大时,所有人一定在同一个城镇里。

我们预处理出 \(suma_i\) 表示各城镇庇护所人数的前缀和、\(sumv_i\) 表示 \(i\times a_i\) 的前缀和,利用二分可以求出 \(d_i\) 表示所有人都在 \(i\) 时,有人进入的庇护所的半径。

注意到对于询问区间 \([l,r]\),若人群聚集在 \([l,\frac{l+r}2]\) 的城镇中,则 \(r\) 对答案不造成影响;否则 \(l\) 对答案不造成影响。因此,我们把每个询问拆分成两个形如 \(q_{l,r}\) 的形式,表示在 \([l,r]\) 中找一个城镇,对应端点限制为 \(l\) 的最大答案,将 \(a\) 序列翻转后两个部分实际相同,对两部分的答案取 \(\max\) 即可。

\(f_{l,j}\) 表示端点位置为 \(l\),人群聚集在 \(j\) 的最小花费。不难通过预处理出的信息 \(O(1)\) 求得一个 \(f\) 的值。我们有 \(q_{l,r}=\max_{x\in[l,r]}f_{l,x}\)

首先,我们发现 \(f_{l-1,x-1}-f_{l,x-1}\geq f_{l-1,x}-f_{l,x}\),原因是 \(x\)\(l\) 越近,受到端点移动影响、需要增大移动距离的人就越多。对上式移项后可得出决策单调性:\(l\) 越小,\(q_{l,r}\) 对应的最优转移点 \(x\) 越小。注意到每个 \(q\) 的决策点是有区间限制的,即必须在 \([l,r]\) 内,因此我们需要在决策单调性外层套一个线段树分治,将每个询问拍到该区间对应的 \(\log\) 个线段树节点上去,在每个节点上,对该节点对应的决策点区间做分治,即可去除总的区间的限制。

总复杂度 \(O(n\log^2n)\)

#include<bits/stdc++.h>
#define rep(i,j,k) for(int i=j;i<=k;i++)
#define repp(i,j,k) for(int i=j;i>=k;i--)
#define ls(x) (x<<1)
#define rs(x) ((x<<1)|1)
#define mp make_pair
#define sec second
#define fir first
#define pii pair<int,int>
#define lowbit(i) i&-i
#define int long long
using namespace std;
typedef long long ll;
const int N=2e5+5,M=5e5+5,S=(1<<15)+5,inf=(ll)1e18+7,mo=1e9+7;
const double eps=1e-8;
void read(int &p){
	int x=0,w=1;
	char ch=0;
	while(!isdigit(ch)){
		if(ch=='-')w=-1;
		ch=getchar();
	}
	while(isdigit(ch)){
		x=(x<<1)+(x<<3)+ch-'0';
		ch=getchar();
	}
	p=x*w;
}
int n,a[N],s,m;
int d[N],sumv[N],suma[N];
struct query{
	int l,r,id;
	friend bool operator<(query x,query y){
		return x.l<y.l;
	}
}q[N];
int getsum(int p,int dim){
	int l=p-dim+1,r=p+dim-1;
	if(l<1||r>n)return inf;
	return suma[r]-suma[l-1];
}
pii nq[N]; 
int getv(int l,int x){
	if(x-l+1>=d[x]){
		int posl=x-d[x]+1,posr=x+d[x]-1,pl=max(suma[x-1]-suma[posl],0ll),pr=max(suma[posr-1]-suma[x],0ll);
		int res=pl*x-(sumv[x-1]-sumv[posl])+(sumv[posr-1]-sumv[x])-pr*x;
		return res+(d[x]-1)*max(s-pl-pr-a[x],0ll);
	}
	else{
		int posl=l,posr=x+(x-l),pl=suma[x-1]-suma[posl-1],pr=suma[posr]-suma[x];
		int res=pl*x-(sumv[x-1]-sumv[posl-1])+(sumv[posr]-sumv[x])-pr*x;
		return res+(x-l+1)*max((s-pl-pr-a[x]),0ll);
	}
}
int ans[N];
struct seg{//线段树分治 
	vector<pii>t[4*N];
	void build(int x,int le,int ri){
		t[x].clear();
		if(le==ri)return;
		int mid=(le+ri)>>1;
		build(ls(x),le,mid),build(rs(x),mid+1,ri);
	}
	void insert(int x,int le,int ri,int ql,int qr,int id){
		if(ql>qr)return;
		if(ql<=le&&qr>=ri){
			t[x].push_back(mp(ql,id));
			return;
		}
		int mid=(le+ri)>>1;
		if(ql<=mid)insert(ls(x),le,mid,ql,qr,id);
		if(qr>mid)insert(rs(x),mid+1,ri,ql,qr,id);
	}
	void solve_dp(int x,int ql,int qr,int le,int ri){
		if(ql>qr)return;
		if(le==ri){
			rep(i,ql,qr)
				ans[t[x][i].sec]=max(ans[t[x][i].sec],getv(t[x][i].fir,le));
			return;
		}
		int mid=(ql+qr)>>1,maxn=0,maxp=le;
		rep(i,le,ri){
			int res=getv(t[x][mid].fir,i);
			if(res>maxn)maxn=res,maxp=i;
		}
		ans[t[x][mid].sec]=max(ans[t[x][mid].sec],maxn);
		solve_dp(x,ql,mid-1,le,maxp),solve_dp(x,mid+1,qr,maxp,ri);
	}
	void dfs(int x,int le,int ri){
		solve_dp(x,0,(int)t[x].size()-1,le,ri);
		if(le==ri)return;
		int mid=(le+ri)>>1;
		dfs(ls(x),le,mid),dfs(rs(x),mid+1,ri);
	}
}T;
void solve(){
	T.build(1,1,n);
	rep(i,1,n)
	    suma[i]=sumv[i]=d[i]=0;
	rep(i,1,n)
	    suma[i]=suma[i-1]+a[i],sumv[i]=sumv[i-1]+i*a[i];
	rep(i,1,n){
		int le=1,ri=min(n-i+1,i)+1;
		while(le<=ri){
			int mid=(le+ri)>>1;
			if(getsum(i,mid)>=s)d[i]=mid,ri=mid-1;
			else le=mid+1;
		}
	}
	sort(q+1,q+m+1);
	rep(i,1,m)
	    T.insert(1,1,n,q[i].l,q[i].r,q[i].id);
	T.dfs(1,1,n);
}
signed main(){
	read(n),read(s);
	rep(i,1,n)
	    read(a[i]);
	read(m);
	rep(i,1,m)
	    read(nq[i].fir),read(nq[i].sec);
	rep(i,1,m){
		int mid=(nq[i].fir+nq[i].sec)>>1;
		q[i]=(query){nq[i].fir,mid,i};
	}
	solve();
	reverse(a+1,a+n+1);
	rep(i,1,m){
		int mid=(nq[i].fir+nq[i].sec)>>1;
		int x=mid+1,y=nq[i].sec;
		q[i]=(query){n-y+1,n-x+1,i};
	}
	solve();
	rep(i,1,m)
	    printf("%lld\n",ans[i]);
	return 0;
}

103469J Joke

题意:给定一个完整排列 \(p\) 和排列 \(q\) 的一部分,设 01 串 \(s\) 合法当且仅当存在 \(2\times n\) 的矩阵 \(M\),满足 \(1\sim 2n\) 在其中均恰好出现一次、\(M_1\)\(p\) 的相对大小关系相同、\(M_2\)\(q\) 的相对大小相同、且 \(s_i=0\) 当且仅当 \(M_{1,i}<M_{2,i}\)。设 \(f_{p,q}\) 表示满足上述条件的合法 01 串数目。对于所有可能的 \(q\),求 \(f_{p,q}\) 的和。\(n\leq 100\)

key words:找性质 dp

不妨设 \(p_i=i\)\(q\) 的已知位置相应交换即可。

我们对 \(M\) 中的每个元素,从小的向大的连边。则第一排会连出一条从左往右的链,当 \(q\) 确定时,也会连成一条值从小到大的链。此时我们需要在每个位置确定两个元素之间箭头的方向,使得最后的图上无环。

不妨先考虑 \(q\) 确定的情况。你发现无环的限制等价于不存在一个 \(i<j\),使得 \(q_i>q_j\)、且 \(i\) 处为上箭头(指向第一排),\(j\) 处为下箭头。假设从左往右遍历每个位置,则 \(q\) 的上箭头子序列的前缀最大值位置会对后面产生限制,形如 \(q\) 小于 \(v\) 的位置不能是下箭头。

不妨枚举确定上箭头子序列的前缀最大值位置,这些位置的 \(q\) 显然需要单增。你会发现此时,其它位置的箭头方向被唯一确定了。具体地,对于位置 \(i\),设其上一个前缀最大值为 \(q_j=x\),则若 \(q_i<x\),那么此处不能填下箭头,否则会成环;若 \(q_i>x\),那么此处不能填上箭头,否则该位置也是前缀最大值,不满足限制。因此,每个前缀最大值位置的情况会对应一个 01 串,而显然一个 01 串对应一个前缀最大值位置集合,故两者一一对应,合法 01 串数量即为 \(q\) 的上升子序列(可以为空)数量

这个东西直接 dp 解决。设 \(dp_{i,j,k}\) 表示考虑到位置 \(i\),前面有 \(j\)未确定的位置被确定为前缀最大值位置,上一个前缀最大值为 \(k\) 的方案数。对于未确定的位置,若是前缀最大位置,则枚举它填什么值,否则不管,最后乘全排列即可。转移是 \(O(n)\) 的,因此最后做到 \(O(n^4)\)

#include<bits/stdc++.h>
#define rep(i,j,k) for(int i=j;i<=k;i++)
#define repp(i,j,k) for(int i=j;i>=k;i--)
#define ls(x) (x<<1)
#define rs(x) ((x<<1)|1)
#define mp make_pair
#define sec second
#define fir first
#define pii pair<int,int>
#define lowbit(i) i&-i
#define int long long 
using namespace std;
typedef long long ll;
const int N=105,M=105,S=10,inf=2e9+7,mo=998244353;
void read(int &p){
	int x=0,w=1;
	char ch=0;
	while(!isdigit(ch)){
		if(ch=='-')w=-1;
		ch=getchar();
	}
	while(isdigit(ch)){
		x=(x<<1)+(x<<3)+ch-'0';
		ch=getchar();
	}
	p=x*w;
}
int n,p[N],q[N],ans,cnte;
int exi[N],jc[N];
int dp[N][N][N];
signed main(){
    read(n);
    rep(i,1,n)
        read(p[i]);
    rep(i,1,n)
        read(q[p[i]]),exi[q[p[i]]]=1;
    jc[0]=1;
    rep(i,1,n)
        jc[i]=jc[i-1]*i%mo;
    sort(p+1,p+n+1);
    rep(i,1,n)
        cnte+=(!exi[i]);
    dp[0][0][0]=1;
    rep(i,1,n){
        rep(j,0,i){
            rep(k,0,n){
                dp[i][j][k]=dp[i-1][j][k];
                if(q[i]){
                    if(q[i]!=k)continue;
                    rep(l,0,k-1)
                        dp[i][j][k]+=dp[i-1][j][l],dp[i][j][k]%=mo;
                }
                else{
                    if(exi[k]||!j)continue;
                    rep(l,0,k-1)
                        dp[i][j][k]+=dp[i-1][j-1][l],dp[i][j][k]%=mo;
                }
            }
        }
    }
    int ans=0;
    rep(i,0,n){
        rep(j,0,n)
            ans+=dp[n][i][j]*jc[cnte-i]%mo,ans%=mo;
    }
    printf("%lld\n",ans);
    return 0;
}

102962E Rooted MST

题意:给定有 \(n+m\) 条边、\(n+1\) 个点的图,前 \(n\) 条边连接点 \(0\)\(i\),边权为 \(a_i\);后 \(m\) 条边连接点 \(u_i,v_i(u,v\in[1,n])\),边权为 \(w_i\)。有 \(q\) 次询问,每次询问给定 \(x,v\) 表示将 \(0\)\(x\) 的边的边权改为 \(v\),并询问当前最小生成树。

首先考虑后 \(m\) 条边将 \(1\sim n\) 连成一条链的情况。我们对链开线段树,当我们考虑完 \([l,r]\) 内的点的连边时,所有满足 \(i\in(l,r)\) 的点都必须和 \(0\) 连通。于是线段树上每个节点维护 \(t_{0/1,0/1}\) 表示这个节点最左侧和最右侧的的点有没有和 \(0\) 连通,合并是容易的。需要注意的细节是对于叶子节点,\(t_{0,1}\)\(t_{1,0}\) 都应当为 \(0\) 而非 \(a\),因为这叶子点在合并时等价于一个边缘的点。

对于一般情况,我们考虑其能否找到一种重构图的方式,使得原图和新图的最小生成树一致。考虑原图 \(m\) 条边做 Kruskal 的过程,实际等价于每次把两个连通块合并到一起了。我们对每个连通块维护一条等效链,则每次合并时就把链的头尾拼接到一起;对于在 \(m\) 条边的情况下不连通的部分,我们用权值为 \(\infty\) 的边连起来即可。其等效原因是显然的:首先我们可以只考虑非 \(0\) 点之间的连边。对于这些边,假设有一条新边被选中,则此时在原图中对应的两个连通块在此刻也一定已经形成,因而这条边对 Kruskal 过程的影响与原图相同。同时,原图的最小生成树边显然不会在这些边之外。

于是在对 \(m\) 条边 Kruskal 的过程中求出 \(n\) 个点的等效链,对这个链维护操作即可。复杂度 \(O(q\log n+m\log m)\)

#include<bits/stdc++.h>
#define rep(i,j,k) for(int i=j;i<=k;i++)
#define repp(i,j,k) for(int i=j;i>=k;i--)
#define ls(x) (x<<1)
#define rs(x) ((x<<1)|1)
#define mp make_pair
#define sec second
#define fir first
#define pii pair<int,int>
#define lowbit(i) i&-i
#define int long long
#define double long double
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int N=3e5+5,M=105,S=(1<<15)+5,inf=(ll)1e18+7,mo=998244353;
const double eps=1e-8;
void read(int &p){
	int x=0,w=1;
	char ch=0;
	while(!isdigit(ch)){
		if(ch=='-')w=-1;
		ch=getchar();
	}
	while(isdigit(ch)){
		x=(x<<1)+(x<<3)+ch-'0';
		ch=getchar();
	}
	p=x*w;
}
int n,m,q;
struct edge{
    int u,v,w;
    friend bool operator<(edge x,edge y){
        return x.w<y.w;
    }
}e[N];
int p[N],a[N],v[N],pos[N];
struct seg{
    int t[4*N][2][2];
    void pushup(int x,int le,int ri){
        rep(i,0,1){
            rep(j,0,1)
                t[x][i][j]=inf;
        }
        int mid=(le+ri)>>1;
        rep(i,0,1){
            rep(j,0,1){
                rep(k,0,1){
                    rep(l,0,1){
                        if(!(k|l))continue;
                        else if(k&l)t[x][i][j]=min(t[x][i][j],t[ls(x)][i][k]+t[rs(x)][l][j]);
                        else t[x][i][j]=min(t[x][i][j],t[ls(x)][i][k]+t[rs(x)][l][j]+v[mid]);
                    }
                }
            }
        }
    }
    void build(int x,int le,int ri){
        if(le==ri){
            t[x][0][1]=t[x][1][0]=t[x][0][0]=0;
            t[x][1][1]=a[p[le]];
            return;
        }
        int mid=(le+ri)>>1;
        build(ls(x),le,mid),build(rs(x),mid+1,ri);
        pushup(x,le,ri);
    }
    void modify(int x,int le,int ri,int p,int val){
        if(le==ri){
            t[x][0][1]=t[x][1][0]=t[x][0][0]=0;
            t[x][1][1]=val;
            return;
        }
        int mid=(le+ri)>>1;
        if(p<=mid)modify(ls(x),le,mid,p,val);
        else modify(rs(x),mid+1,ri,p,val);
        pushup(x,le,ri);
    }
}T;
struct bcj{
    int fa[N];
    vector<int>ev[N],pt[N];
    void init(){
        rep(i,1,n)
            fa[i]=i,pt[i].push_back(i);
    }
    int find(int x){
        if(x==fa[x])return x;
        return fa[x]=find(fa[x]);
    }
    void merge(int x,int y,int val){
        x=find(x),y=find(y);
        if(x==y)return;
        if(pt[x].size()<pt[y].size())swap(x,y);
        fa[y]=x,ev[x].push_back(val);
        for(auto j:pt[y])
            pt[x].push_back(j);
        for(auto j:ev[y])
            ev[x].push_back(j);
    }
}B;
signed main(){
    read(n),read(m);
    rep(i,1,n)
        read(a[i]);
    rep(i,1,m)
        read(e[i].u),read(e[i].v),read(e[i].w);
    sort(e+1,e+m+1);
    B.init();
    rep(i,1,m)
        B.merge(e[i].u,e[i].v,e[i].w);
    rep(i,1,n-1)
        B.merge(i,i+1,inf);
    int rt=B.find(1),cntp=0;
    for(auto i:B.pt[rt])
        p[++cntp]=i,pos[i]=cntp;
    cntp=0;
    for(auto i:B.ev[rt])
        v[++cntp]=i;
    T.build(1,1,n);
    read(q);
    while(q--){
        int x,val;
        read(x),read(val);
        T.modify(1,1,n,pos[x],val);
        printf("%lld\n",T.t[1][1][1]);
    }
    return 0;
}
posted @ 2024-07-16 19:02  烟山嘉鸿  阅读(137)  评论(0)    收藏  举报