codeforces contest 1140(D~G)

前言

A~C不想写博客了,就不写了,后面的题还是要推一推的,所以写一下

CF 1140 D

题目大意
给出一个正多边形,顶点按顺序标号为 1 1 1~ n n n,一个三角划分的权值是每个三角形三个顶点的编号乘积之和
求出一个三角划分使得这个三角划分的权值是所有的中最小的
n ≤ 500 n\le500 n500
题解:
区间dp即可,挺方便的,复杂度 O ( n 3 ) \mathcal O(n^3) O(n3)
但是事实上,这题是个结论题,可以直接 O ( 1 ) \mathcal O(1) O(1)计算的
我们来进行一下推导
我们考虑 1 1 1 n n n的边1-n,假设其所在的三角划分是 Δ \Delta Δ 1-x-n
如果 x ≠ n − 1 x\neq n-1 x̸=n1,那么边x-n一定在另一个三角划分 Δ \Delta Δ x-k-n( k > x k>x k>x)中
我们考虑这两个三角划分构成的四边形(此处贴了一幅图,可以根据图来理解下面的证明
我们把这两个三角划分
Δ \Delta Δ 1-x-n+ Δ \Delta Δ x-k-n改成 Δ \Delta Δ 1-x-k+ Δ \Delta Δ 1-k-n
我们发现值会严格变小,即
1 ∗ x ∗ n + x ∗ k ∗ n > 1 ∗ x ∗ k + 1 ∗ k ∗ n 1*x*n+x*k*n>1*x*k+1*k*n 1xn+xkn>1xk+1kn
这个不等式很显然吧,是由两个不等式加在一起的
x ∗ k ∗ n > 1 ∗ k ∗ n x*k*n>1*k*n xkn>1kn 1 ∗ x ∗ n > 1 ∗ x ∗ k 1*x*n>1*x*k 1xn>1xk
在这里插入图片描述
这样不停操作,我们发现最后 x x x就是 n − 1 n-1 n1了,然后又转化成子问题(即 n n n减小了 1 1 1),所以最后的答案一定是所有三角划分都有 1 1 1这个顶点
那么答案就是
∏ i = 2 n − 1 i ∗ ( i + 1 ) \prod_{i=2}^{n-1}i*(i+1) i=2n1i(i+1)
直接转化成
∏ i = 2 n − 1 i 2 + i \prod_{i=2}^{n-1}i^2+i i=2n1i2+i
直接平方和公式和等差数列求和公式代入即可

#include<bits/stdc++.h>
typedef long long ll;
#define rg register
template <typename T> inline void read(T&x){char cu=getchar();x=0;bool fla=0;while(!isdigit(cu)){if(cu=='-')fla=1;cu=getchar();}while(isdigit(cu))x=x*10+cu-'0',cu=getchar();if(fla)x=-x;}
template <typename T> inline void printe(const T x){if(x>=10)printe(x/10);putchar(x%10+'0');}
template <typename T> inline void print(const T x){if(x<0)putchar('-'),printe(-x);else printe(x);}
template <typename T> inline T min(const T a,const T b){return a<b?a:b;}
template <typename T> inline T max(const T a,const T b){return a>b?a:b;}
int n; 
int main()
{
	read(n);
	print((n-1)*n*(n*2-1)/6+n*(n-1)/2-2);
	return 0;
}

CF 1140 E

题目大意:给定一个值域为 k k k的序列,有些位置还没有填上数,求有多少种填数方式使得这个序列不存在非1奇长度回文子串
n , k ≤ 2 ⋅ 1 0 5 n,k\le2·10^5 n,k2105
题解: 这里有个长度回文子串,这是非常好的性质,我们发现如果对于任何 i i i均满足 a i ≠ a i + 2 a_i\neq a_{i+2} ai̸=ai+2,那么这个序列就是合法的
那么我们可以把奇数、偶数位单独拖出来,就变成了子问题,有多少种填法使得没有相邻数相同(这个可以直接dp做),奇偶串答案相乘即可
复杂度 O ( n ) \mathcal O(n) O(n)

#include<bits/stdc++.h>
typedef long long ll;
#define rg register
template <typename T> inline void read(T&x){char cu=getchar();x=0;bool fla=0;while(!isdigit(cu)){if(cu=='-')fla=1;cu=getchar();}while(isdigit(cu))x=x*10+cu-'0',cu=getchar();if(fla)x=-x;}
template <typename T> inline void printe(const T x){if(x>=10)printe(x/10);putchar(x%10+'0');}
template <typename T> inline void print(const T x){if(x<0)putchar('-'),printe(-x);else printe(x);}
template <typename T> inline T min(const T a,const T b){return a<b?a:b;}
template <typename T> inline T max(const T a,const T b){return a>b?a:b;}
const int mod=998244353;
int n,k,a[200001],b[200001],ta,tb;
ll A[200001],B[200001],C[200001];//A,A//A,B//A,non
ll ans=1;
int main()
{
	read(n),read(k);
	for(rg int i=1;i<=n;i++)if(i&1)read(a[++ta]);else read(b[++tb]);
	A[1]=k-1,B[1]=k-2,C[1]=k-1;
	A[0]=B[0]=C[0]=1;
	for(rg int i=2;i<=n;i++)
	{
		A[i]=B[i-1]*(k-1)%mod;
		B[i]=(B[i-1]*(k-2)+A[i-1])%mod;
		C[i]=C[i-1]*(k-1)%mod;
	}
	for(rg int i=1;i<=ta;i++)
		if(a[i]==-1)
		{
			int j=i;
			while(j<ta&&a[j+1]==-1)j++;
			if(i==1&&j==ta)ans=ans*C[ta-1]%mod*k%mod;
			else if(i==1||j==ta)ans=ans*C[j-i+1]%mod;
			else if(a[i-1]==a[j+1])ans=ans*A[j-i+1]%mod;
			else ans=ans*B[j-i+1]%mod;
			i=j+1;
		}
		else if(i!=ta&&a[i+1]!=-1&&a[i]==a[i+1])ans=0;
	for(rg int i=1;i<=tb;i++)
		if(b[i]==-1)
		{
			int j=i;
			while(j<tb&&b[j+1]==-1)j++;
			if(i==1&&j==tb)ans=ans*C[tb-1]%mod*k%mod;
			else if(i==1||j==tb)ans=ans*C[j-i+1]%mod;
			else if(b[i-1]==b[j+1])ans=ans*A[j-i+1]%mod;
			else ans=ans*B[j-i+1]%mod;
			i=j+1;
		}
		else if(i!=tb&&b[i+1]!=-1&&b[i]==b[i+1])ans=0;
	print(ans);
	return 0;
}

CF 1140 F

题目大意:在平面直角坐标系中,如果有3个点能作为一个矩形(边平行于坐标轴)的三个顶点,那么我们可以把这个矩形的第四个点也加入点集,直到不能再加入
设当前点集为 S S S,不断的进行操作使得点集变为 T T T
现在给出若干点集 S S S求每个对应的 ∣ T ∣ |T| T
给出的方式是每次加入一个点或者删除一个点
q ≤ 3 ⋅ 1 0 5 , 1 ≤ x i , y i ≤ 3 ⋅ 1 0 5 q\le3·10^5,1\le x_i,y_i\le3·10^5 q31051xi,yi3105
题解:
这题真爽
我们考虑假如只有加点操作
我们对于所有坐标点,如果有两个点横坐标/纵坐标相同,那么我们视为这两个点是联通的
要求的就是所有联通快的不同横坐标数量乘以不同纵坐标数量的和
我们发现这个东西大概要开set之类的维护,不是很方便,并且在贡献答案的时候会有一些特判(所有单独点的联通快显然都贡献1,但是如果这个点根本没有,那就贡献0)
方便起见,考虑优化
我们拆点,把横坐标的位置、纵坐标的位置拆开,这样每个点的加入视为连一条边,这个时候维护不同横坐标数量、不同纵坐标数量就很方便了,直接加就好了,还不会有贡献答案的问题(我们发现这个时候单独一个点的贡献是0,而加入一条连向一个横坐标和一个纵坐标的边时能贡献1)
考虑如何解决删点的情况
我们发现如果一个点被删除了,那么他贡献答案的地方一定是一个区间,我们直接用线段树维护区间即可,那么一次插入会到 O ( l o g q ) \mathcal O(logq) O(logq)条线段树上
考虑如何处理出每个叶子节点的答案,经典套路,直接dfs即可,然后用可持久化并查集维护并查集,由于不能路径压缩、只能按秩合并,所以并查集复杂度是 O ( l o g q ) \mathcal O(logq) O(logq)
总复杂度 O ( q l o g 2 q ) \mathcal O(qlog^2q) O(qlog2q)
本题后文有彩蛋,请一定要观看

#include<bits/stdc++.h>
typedef long long ll;
#define rg register
template <typename T> inline void read(T&x){char cu=getchar();x=0;bool fla=0;while(!isdigit(cu)){if(cu=='-')fla=1;cu=getchar();}while(isdigit(cu))x=x*10+cu-'0',cu=getchar();if(fla)x=-x;}
template <typename T> inline void printe(const T x){if(x>=10)printe(x/10);putchar(x%10+'0');}
template <typename T> inline void print(const T x){if(x<0)putchar('-'),printe(-x);else printe(x);}
template <typename T> inline T min(const T a,const T b){return a<b?a:b;}
template <typename T> inline T max(const T a,const T b){return a>b?a:b;}
const int MAX=2097152;
int q,l[MAX],r[MAX],mid[MAX];
std::vector<std::pair<int,int> >irt[MAX];
std::vector<std::pair<int,int> >::iterator pos;
void ini(const int root,const int ll,const int rr)
{
	l[root]=ll,r[root]=rr,mid[root]=(ll+rr)>>1;
	if(ll==rr)return;
	ini(root<<1,ll,mid[root]),ini(root<<1|1,mid[root]+1,rr);
}
void insert(const int root,const int wanl,const int wanr,std::pair<int,int>V)
{
	if(wanl==l[root]&&wanr==r[root])
	{
		irt[root].push_back(V);
		return;
	}
	if(wanr<=mid[root])insert(root<<1,wanl,wanr,V);
	else if(wanl>mid[root])insert(root<<1|1,wanl,wanr,V);
	else insert(root<<1,wanl,mid[root],V),insert(root<<1|1,mid[root]+1,wanr,V);
}
struct P
{
	int a,b,t;
	bool operator <(const P B)const
	{
		return a==B.a?b<B.b:a<B.a;
	}
};
std::set<P>Q;
std::set<P>::iterator Pos;
struct info
{
	int v,a,b;
	void clear()
	{
		v=a=b=0;
	}
};
struct node
{
	node*lson,*rson;info v;
}t[12600021],*root[600001],empty,*Null;
int usdto[600001],id;
node*newnode()
{
	node*R=&t[++usdto[id]];
	R->lson=R->rson=Null,R->v.clear();
	return R;
}
int pl;info val;
void INSERT(node*las,node*dq,const int root)
{
	if(l[root]==r[root])
	{
		dq->v=val;
		return;
	}
	if(pl<=mid[root])INSERT(las->lson,dq->lson=newnode(),root<<1),dq->rson=las->rson;
	else INSERT(las->rson,dq->rson=newnode(),root<<1|1),dq->lson=las->lson;
}
info RES;
void SEARCH(node*dq,const int root,const int pl)
{
	if(l[root]==r[root])
	{
		if(dq->v.v==0)RES=(info){pl,pl<=300000,pl>300000};
		else RES=dq->v;
		return;
	}
	if(pl<=mid[root])SEARCH(dq->lson,root<<1,pl);
	else SEARCH(dq->rson,root<<1|1,pl);
}
ll ans[600001];
info find(const int x)
{
	SEARCH(root[id],1,x);
	info u=RES;
	if(u.v==x)return u;
	return find(u.v);
}
void dodo(std::pair<int,int> V)
{
//	printf("dodo %d %d %d\n",ans[id],V.first,V.second);
	V.second+=300000;
	info u=find(V.first),v=find(V.second);
	if(u.v==v.v)id+=2,usdto[id]=usdto[id-2],root[id]=root[id-2],ans[id]=ans[id-2];
	else
	{
		if(u.a+u.b<v.a+v.b)std::swap(u,v);
		id++,usdto[id]=usdto[id-1];
		root[id]=newnode();
		pl=v.v,val=(info){u.v,0,0};
		INSERT(root[id-1],root[id],1);
		id++,usdto[id]=usdto[id-1];
		root[id]=newnode();
		pl=u.v,val=(info){u.v,u.a+v.a,u.b+v.b};
		INSERT(root[id-1],root[id],1);
		ans[id]=ans[id-2];
	//	printf("(%lld %d %d %d %d)\n",ans[id],u.a,u.b,v.a,v.b);
		ans[id]+=(ll)(u.a+v.a)*(u.b+v.b)-(ll)u.a*u.b-(ll)v.a*v.b;
	}
}
void undo()
{
//	puts("undo");
	id-=2;
}
void dfs(const int root)
{
	if(l[root]>q)return;
//	printf(">>%d %d\n",l[root],r[root]);
	for(pos=irt[root].begin();pos!=irt[root].end();pos++)dodo(*pos);
	if(l[root]==r[root])print(ans[id]),putchar(' ');
	else dfs(root<<1),dfs(root<<1|1);
	for(pos=irt[root].begin();pos!=irt[root].end();pos++)undo();
//	printf("<<%d %d\n",l[root],r[root]);
}
int main()
{
	empty.lson=empty.rson=Null=&empty;
	root[0]=newnode();
	read(q);
	ini(1,1,600000);
	for(rg int i=1;i<=q;i++)
	{
		P u;
		read(u.a),read(u.b);
		if((Pos=Q.find(u))==Q.end())
		{
			u.t=i;
			Q.insert(u);
		}
		else
		{
			insert(1,(*Pos).t,i-1,std::make_pair((*Pos).a,(*Pos).b));
			Q.erase(Pos);
		}
	}
	while(Q.size())
	{
		Pos=Q.begin();
		insert(1,(*Pos).t,q,std::make_pair((*Pos).a,(*Pos).b));
		Q.erase(Pos);
	}
	dfs(1);
	return 0;
}

xswl,我以为可持久化并查集是对的,其实是3个log的,T成大傻逼
上面贴出的是TLE代码
并查集是其实是可以 O ( 1 ) \mathcal O(1) O(1)撤销的,只能重写

#include<bits/stdc++.h>
typedef long long ll;
#define rg register
template <typename T> inline void read(T&x){char cu=getchar();x=0;bool fla=0;while(!isdigit(cu)){if(cu=='-')fla=1;cu=getchar();}while(isdigit(cu))x=x*10+cu-'0',cu=getchar();if(fla)x=-x;}
template <typename T> inline void printe(const T x){if(x>=10)printe(x/10);putchar(x%10+'0');}
template <typename T> inline void print(const T x){if(x<0)putchar('-'),printe(-x);else printe(x);}
template <typename T> inline T min(const T a,const T b){return a<b?a:b;}
template <typename T> inline T max(const T a,const T b){return a>b?a:b;}
const int MAX=2097152;
int q,l[MAX],r[MAX],mid[MAX];
std::vector<std::pair<int,int> >irt[MAX];
std::vector<std::pair<int,int> >::iterator pos;
void ini(const int root,const int ll,const int rr)
{
	l[root]=ll,r[root]=rr,mid[root]=(ll+rr)>>1;
	if(ll==rr)return;
	ini(root<<1,ll,mid[root]),ini(root<<1|1,mid[root]+1,rr);
}
void insert(const int root,const int wanl,const int wanr,std::pair<int,int>V)
{
	if(wanl==l[root]&&wanr==r[root])
	{
		irt[root].push_back(V);
		return;
	}
	if(wanr<=mid[root])insert(root<<1,wanl,wanr,V);
	else if(wanl>mid[root])insert(root<<1|1,wanl,wanr,V);
	else insert(root<<1,wanl,mid[root],V),insert(root<<1|1,mid[root]+1,wanr,V);
}
struct P
{
	int a,b,t;
	bool operator <(const P B)const
	{
		return a==B.a?b<B.b:a<B.a;
	}
};
std::set<P>Q;
std::set<P>::iterator Pos;
ll ans;
int bcj[600001],ltot[600001],rtot[600001];
int find(const int x)
{
	if(x==bcj[x])return x;
	return find(bcj[x]);
}
int cha[600001],chb[600001],opt;
void dodo(std::pair<int,int> V)
{
	V.second+=300000;
	int u=find(V.first),v=find(V.second);
	opt++;
	if(u==v)cha[opt]=0;
	else
	{
		if(ltot[u]+rtot[u]<ltot[v]+rtot[v])std::swap(u,v);
		bcj[v]=u;
		ans+=(ll)(ltot[u]+ltot[v])*(rtot[u]+rtot[v])-(ll)ltot[u]*rtot[u]-(ll)ltot[v]*rtot[v];
		ltot[u]+=ltot[v];
		rtot[u]+=rtot[v];
		cha[opt]=u,chb[opt]=v;
	}
}
void undo()
{
	if(cha[opt])
	{
		int u=cha[opt],v=chb[opt];
		ltot[u]-=ltot[v];
		rtot[u]-=rtot[v];
		ans-=(ll)(ltot[u]+ltot[v])*(rtot[u]+rtot[v])-(ll)ltot[u]*rtot[u]-(ll)ltot[v]*rtot[v];
		bcj[v]=v;
	}
	opt--;
}
void dfs(const int root)
{
	for(pos=irt[root].begin();pos!=irt[root].end();pos++)dodo(*pos);
	if(l[root]==r[root])print(ans),putchar(' ');
	else dfs(root<<1),dfs(root<<1|1);
	for(pos=irt[root].begin();pos!=irt[root].end();pos++)undo();
}
int main()
{
	for(rg int i=1;i<=600000;i++)
	{
		bcj[i]=i;
		if(i<=300000)ltot[i]++;
		else rtot[i]++;
	}
	read(q);
	ini(1,1,q);
	for(rg int i=1;i<=q;i++)
	{
		P u;
		read(u.a),read(u.b);
		if((Pos=Q.find(u))==Q.end())
		{
			u.t=i;
			Q.insert(u);
		}
		else
		{
			insert(1,(*Pos).t,i-1,std::make_pair((*Pos).a,(*Pos).b));
			Q.erase(Pos);
		}
	}
	while(Q.size())
	{
		Pos=Q.begin();
		insert(1,(*Pos).t,q,std::make_pair((*Pos).a,(*Pos).b));
		Q.erase(Pos);
	}
	dfs(1);
	return 0;
}

CF 1140 G

题目大意
给出一棵 n n n个节点的树,然后将这棵树复制一遍,并且复制后与复制前的点连边,两棵树内部分别有树边
这些所有边都有边权
q q q组询问,每次给出两个点,求两个点之间的最短路
n ≤ 3 ⋅ 1 0 5 , q ≤ 6 ⋅ 1 0 5 n\le 3·10^5,q\le6·10^5 n3105,q6105
题解:
看起来好像是求图上最短路
但实际上我们发现,这还是一个树上最短路问题
考虑我们是怎么求树上最短路的——求lca,然后深度减一下即可
对于这道题,我们考虑,最短路一定会经过lca在第一棵树上的点或者是lca在第二棵树上的点,所以我们如果能处理处一个倍增数组 f i , j , k , l f_{i,j,k,l} fi,j,k,l( i , j ∈ [ 1 , n ] , k , l ∈ [ 0 , 1 ] i,j\in[1,n],k,l\in[0,1] i,j[1,n],k,l[0,1])表示编号为 i i i的点在第 k k k棵树上到其的 2 j 2^j 2j祖先在第 l l l棵树上的最短路
考虑如何转移,我们发现只需要求出所有的 g i g_i gi表示树上 i i i这个节点所对应的两个节点之间的最短路长度即可
我们发现 g g g可以两遍dfs求得,然后 f f f也可以求出,最后就只要对于每个询问倍增就好了
复杂度 O ( n l o g n ) \mathcal O(nlogn) O(nlogn)

#include<bits/stdc++.h>
typedef long long ll;
#define rg register
template <typename T> inline void read(T&x){char cu=getchar();x=0;bool fla=0;while(!isdigit(cu)){if(cu=='-')fla=1;cu=getchar();}while(isdigit(cu))x=x*10+cu-'0',cu=getchar();if(fla)x=-x;}
template <typename T> inline void printe(const T x){if(x>=10)printe(x/10);putchar(x%10+'0');}
template <typename T> inline void print(const T x){if(x<0)putchar('-'),printe(-x);else printe(x);}
template <typename T> inline T min(const T a,const T b){return a<b?a:b;}
template <typename T> inline T max(const T a,const T b){return a>b?a:b;}
template <typename T> inline void mind(T&a,const T b){a=a<b?a:b;}
const int maxn=300001,maxm=600001;
int n,q,dep[maxn];
int head[maxn],nxt[maxm],tow[maxm],tmp;ll vau1[maxm],vau2[maxm];
inline void addb(const int u,const int v,const ll w1,const ll w2)
{
	tmp++;
	nxt[tmp]=head[u];
	head[u]=tmp;
	tow[tmp]=v;
	vau1[tmp]=w1;
	vau2[tmp]=w2;
}
ll g[maxn];
void dfs1(const int u,const int fa)
{
	for(rg int i=head[u];i;i=nxt[i])
	{
		const int v=tow[i];
		if(v==fa)continue;
		dep[v]=dep[u]+1;
		dfs1(v,u);
		g[u]=min(g[u],vau1[i]+vau2[i]+g[v]);
	}
}
ll f[maxn][21][2][2];int p[maxn][21];
void dfs2(const int u,const int fa)
{
	for(rg int i=head[u];i;i=nxt[i])
	{
		const int v=tow[i];
		if(v==fa)continue;
		g[v]=min(g[v],vau1[i]+vau2[i]+g[u]);
		f[v][0][0][0]=vau1[i];
		f[v][0][0][1]=g[v]+vau2[i];
		mind(f[v][0][0][0],f[v][0][0][1]+g[u]);
		mind(f[v][0][0][1],f[v][0][0][0]+g[u]);
		f[v][0][1][0]=g[v]+vau1[i];
		f[v][0][1][1]=vau2[i];
		mind(f[v][0][1][0],f[v][0][1][1]+g[u]);
		mind(f[v][0][1][1],f[v][0][1][0]+g[u]);
		p[v][0]=u;
		dfs2(v,u);
	}
}
ll dpu[2],dpv[2],dp[2];
void C()
{
	std::swap(dpu[0],dpv[0]);
	std::swap(dpu[1],dpv[1]);
}
void CP(ll*D)
{
	dp[0]=D[0];
	dp[1]=D[1];
}
ll lca(int u,int v)
{
	if(u&1)u=(u+1)>>1,dpu[0]=0,dpu[1]=g[u];
	else u=(u+1)>>1,dpu[0]=g[u],dpu[1]=0;
	if(v&1)v=(v+1)>>1,dpv[0]=0,dpv[1]=g[v];
	else v=(v+1)>>1,dpv[0]=g[v],dpv[1]=0;
	if(dep[u]<dep[v])std::swap(u,v),C();
	const int DEP=dep[u]-dep[v];
	for(rg int i=0;i<=20;i++)
		if(DEP&(1<<i))
		{
			CP(dpu);
			dpu[0]=min(dp[0]+f[u][i][0][0],dp[1]+f[u][i][1][0]);
			dpu[1]=min(dp[0]+f[u][i][0][1],dp[1]+f[u][i][1][1]);
			u=p[u][i];
			mind(dpu[0],dpu[1]+g[u]);
			mind(dpu[1],dpu[0]+g[u]);
		}
	if(u==v)return min(dpu[0]+dpv[0],dpu[1]+dpv[1]);
	for(rg int i=20;i>=0;i--)
		if(p[u][i]!=p[v][i])
		{
			CP(dpu);
			dpu[0]=min(dp[0]+f[u][i][0][0],dp[1]+f[u][i][1][0]);
			dpu[1]=min(dp[0]+f[u][i][0][1],dp[1]+f[u][i][1][1]);
			u=p[u][i];
			mind(dpu[0],dpu[1]+g[u]);
			mind(dpu[1],dpu[0]+g[u]);
			CP(dpv);
			dpv[0]=min(dp[0]+f[v][i][0][0],dpv[1]+f[v][i][1][0]);
			dpv[1]=min(dp[0]+f[v][i][0][1],dpv[1]+f[v][i][1][1]);
			v=p[v][i];
			mind(dpv[0],dpv[1]+g[v]);
			mind(dpv[1],dpv[0]+g[v]);
		}
	CP(dpu);
	dpu[0]=min(dp[0]+f[u][0][0][0],dp[1]+f[u][0][1][0]);
	dpu[1]=min(dp[0]+f[u][0][0][1],dp[1]+f[u][0][1][1]);
	u=p[u][0];
	mind(dpu[0],dpu[1]+g[u]);
	mind(dpu[1],dpu[0]+g[u]);
	CP(dpv);
	dpv[0]=min(dp[0]+f[v][0][0][0],dpv[1]+f[v][0][1][0]);
	dpv[1]=min(dp[0]+f[v][0][0][1],dpv[1]+f[v][0][1][1]);
	v=p[v][0];
	mind(dpv[0],dpv[1]+g[v]);
	mind(dpv[1],dpv[0]+g[v]);
	return min(dpu[0]+dpv[0],dpu[1]+dpv[1]);
}
int main()
{
	read(n);
	for(rg int i=1;i<=n;i++)read(g[i]);
	for(rg int i=1;i<n;i++)
	{
		int u,v;ll w1,w2;read(u),read(v),read(w1),read(w2);
		addb(u,v,w1,w2);
		addb(v,u,w1,w2);
	}
	dfs1(1,0);
	p[1][0]=1,dfs2(1,0);
	for(rg int j=1;j<=20;j++)
		for(rg int i=1;i<=n;i++)
		{
			const int P=p[i][j-1];
			p[i][j]=p[P][j-1];
			const int T=p[i][j];
			f[i][j][0][0]=min(f[i][j-1][0][0]+f[P][j-1][0][0],f[i][j-1][0][1]+f[P][j-1][1][0]);
			f[i][j][0][1]=min(f[i][j-1][0][0]+f[P][j-1][0][1],f[i][j-1][0][1]+f[P][j-1][1][1]);
			mind(f[i][j][0][0],f[i][j][0][1]+g[T]);
			mind(f[i][j][0][1],f[i][j][0][0]+g[T]);
			f[i][j][1][0]=min(f[i][j-1][1][0]+f[P][j-1][0][0],f[i][j-1][1][1]+f[P][j-1][1][0]);
			f[i][j][1][1]=min(f[i][j-1][1][0]+f[P][j-1][0][1],f[i][j-1][1][1]+f[P][j-1][1][1]);
			mind(f[i][j][1][0],f[i][j][1][1]+g[T]);
			mind(f[i][j][1][1],f[i][j][1][0]+g[T]);
		}
	read(q);
	while(q--)
	{
		int u,v;read(u),read(v);
		print(lca(u,v)),putchar('\n');
	}
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值