CSP-S2022退役记

这几天抽空把 \(CSP-S\) 的题改了一下,算是明白我是什么东西了。

反正本次 \(CSP-S\) 连暴力都没能写满,我在知道 \(T3\) 暴力怎么写后觉得太麻烦,就去搞 \(T1\) 了,导致一分也没多拿,\(T4\)\(k=1\) 也没打,我是个废物,估分只有 \(170\),就看 \(CCF\) 的机子牛不牛了。

\(T1\;\;假期计划\)

本题场上一眼最短路预处理,\(DP\) 折半合并,但由于没看出来只需要维护前三大即可,所以应该是只拿了 \(70\) 的暴力。

关于正解,我们考虑题目要求

\(1\to A\to B\to C\to D\to 1\)

我们发现前一半和后一半的形式是一样的,所以我们可以同时处理,最后合并。

我们考虑找出对于每个点 \(i\),距离他不超过 \(k\) 的点且距离 \(1\) 不超过 \(k\) 的点 \(j\) ,然后在这若干个点中找出权值前三大的 \(j\)(其实我们这一步就是找出合法的 \((B,A),(C,D)\) 点对)。我们考虑因为会对我们产生影响的只有与其对称的两个点,因此我们找出权值最大的前三个点就一定能保证找到最优解,我们最后合并即可。

\(code\)

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<set>
using namespace std;
const int N=3000;
int n,m,k;
long long s[N];
int head[N],tot;
struct Node{
    int to,nest;
}bian[N<<3];
void add(int x,int y){
    bian[++tot]=(Node){y,head[x]};
    head[x]=tot;
}
int dis[N][N];
int vis[N];
int q[N],l,r;
void bfs(int st){
    l=1,r=0;
    for(int i=1;i<=n;i++)dis[st][i]=0x3f3f3f3f,vis[i]=0;
    q[++r]=st,vis[st]=1;
    dis[st][st]=0;
    while(l<=r){
        int cc=q[l++];
        for(int i=head[cc];i;i=bian[i].nest){
            int v=bian[i].to;
            if(dis[st][v]>dis[st][cc]+1){
                dis[st][v]=dis[st][cc]+1;
                if(dis[st][v]>=k+1)continue;
                else vis[v]=1,q[++r]=v;
            }
        }
    }
    for(int i=1;i<=n;i++){
        if(dis[st][i]<=k+1)dis[st][i]=1;
        else dis[st][i]=0;
    }
}
int maxk[N][10];
void Insert(int pos,int pos2){
    if(s[pos2]>s[maxk[pos][1]])maxk[pos][3]=maxk[pos][2],maxk[pos][2]=maxk[pos][1],maxk[pos][1]=pos2;
    else if(s[pos2]>s[maxk[pos][2]])maxk[pos][3]=maxk[pos][2],maxk[pos][2]=pos2;
    else if(s[pos2]>s[maxk[pos][3]])maxk[pos][3]=pos2;
}
int main(){
    freopen("holiday.in","r",stdin);
    freopen("holiday.out","w",stdout);
    scanf("%d%d%d",&n,&m,&k);
    for(int i=2;i<=n;i++)scanf("%lld",&s[i]);
    for(int i=1;i<=m;i++){
        int s1=0,s2=0;
        scanf("%d%d",&s1,&s2);
        add(s1,s2),add(s2,s1);
    }
    for(int i=1;i<=n;i++)bfs(i);
    for(int i=2;i<=n;i++){
        for(int j=2;j<=n;j++){
            if(i==j)continue;
            if(dis[i][j]==0)continue;
            if(dis[j][1]==0)continue;
            Insert(i,j);
        }
    }
    long long ans=0;
    for(int i=2;i<=n;i++){
        for(int it1=1;it1<=3;it1++){
            if(maxk[i][it1]==0)break;
            for(int j=2;j<=n;j++){
                if(i==j||j==maxk[i][it1])continue;
                if(dis[i][j]==0)continue;
                for(int it2=1;it2<=3;it2++){
                    if(maxk[j][it2]==0)break;
                    if(i==maxk[j][it2]||j==maxk[j][it2]||maxk[i][it1]==maxk[j][it2])continue;
                    ans=max(ans,s[i]+s[j]+s[maxk[i][it1]]+s[maxk[j][it2]]);
                }
            }
        }
    }
    printf("%lld\n",ans);
    return 0;
}

\(T2\;\;策略游戏\)

场切题,就大力分讨即可

可能被 \(CCF\) 老爷机卡常的 \(code\)

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int M=1e5+10;
const int INF=0x3f3f3f3f;
const long long inf=0x3f3f3f3f3f3f3f3f;
int n,m,q;
int A[M],B[M];
struct Segment_tree{
	#define lson (rt << 1)
	#define rson (rt << 1 | 1)
	struct seg{
		int l,r,maxk,mink,pre_ze,next_ze;
		bool have_zero;
	}tree[M<<2];
	void pushup(int rt){
		tree[rt].have_zero=(tree[lson].have_zero|tree[rson].have_zero);
		tree[rt].maxk=max(tree[lson].maxk,tree[rson].maxk);
		tree[rt].mink=min(tree[lson].mink,tree[rson].mink);
		tree[rt].pre_ze=max(tree[lson].pre_ze,tree[rson].pre_ze);
		tree[rt].next_ze=min(tree[lson].next_ze,tree[rson].next_ze);
	} 
	void build(int rt,int l,int r,int *c){
		tree[rt].l=l,tree[rt].r=r;
		tree[rt].have_zero=0;
		tree[rt].maxk=-INF;
		tree[rt].mink=INF;
		tree[rt].pre_ze=-INF;
		tree[rt].next_ze=INF;
		if(l==r){
			tree[rt].maxk=tree[rt].mink=c[l];
			if(c[l]==0)tree[rt].have_zero=1;
			else if(c[l]<0){
				tree[rt].pre_ze=c[l];
			}
			else tree[rt].next_ze=c[l];
			return ;
		}
		int mid=(l+r)>>1;
		build(lson,l,mid,c);
		build(rson,mid+1,r,c);
		pushup(rt);
	}
	int askmax(int rt,int l,int r){
		if(l<=tree[rt].l&&tree[rt].r<=r)return tree[rt].maxk;
		int mid=(tree[rt].l+tree[rt].r)>>1;
		int ans=-INF;
		if(l<=mid)ans=max(ans,askmax(lson,l,r));
		if(r>mid)ans=max(ans,askmax(rson,l,r));
		return ans;
	}
	int askmin(int rt,int l,int r){
		if(l<=tree[rt].l&&tree[rt].r<=r)return tree[rt].mink;
		int mid=(tree[rt].l+tree[rt].r)>>1;
		int ans=INF;
		if(l<=mid)ans=min(ans,askmin(lson,l,r));
		if(r>mid)ans=min(ans,askmin(rson,l,r));
		return ans;
	}
	bool ze(int rt,int l,int r){
		if(l<=tree[rt].l&&tree[rt].r<=r)return tree[rt].have_zero;
		int mid=(tree[rt].l+tree[rt].r)>>1;
		int ans=0;
		if(l<=mid)ans|=ze(lson,l,r);
		if(r>mid)ans|=ze(rson,l,r);
		return ans; 
	}
	int pre(int rt,int l,int r){
		if(l<=tree[rt].l&&tree[rt].r<=r)return tree[rt].pre_ze;
		int mid=(tree[rt].l+tree[rt].r)>>1;
		int ans=-INF;
		if(l<=mid)ans=max(ans,pre(lson,l,r));
		if(r>mid)ans=max(ans,pre(rson,l,r));
		return ans;
	}
	int next(int rt,int l,int r){
		if(l<=tree[rt].l&&tree[rt].r<=r)return tree[rt].next_ze;
		int mid=(tree[rt].l+tree[rt].r)>>1;
		int ans=INF;
		if(l<=mid)ans=min(ans,next(lson,l,r));
		if(r>mid)ans=min(ans,next(rson,l,r));
		return ans;
	}
}TA,TB; 
void work(int l1,int r1,int l2,int r2){
	int Maxk1=TA.askmax(1,l1,r1),Mink1=TA.askmin(1,l1,r1);
	int Maxk2=TB.askmax(1,l2,r2),Mink2=TB.askmin(1,l2,r2);
	if(Maxk1<0&&Mink1<0&&Maxk2>=0&&Mink2>=0){
		printf("%lld\n",1ll*Maxk1*Maxk2);return ;
	}
	if(Maxk1<0&&Mink1<0&&Maxk2>=0&&Mink2<0){
		printf("%lld\n",1ll*Maxk1*Maxk2);return ;
	}
	if(Maxk1<0&&Mink1<0&&Maxk2<0&&Mink2<0){
		printf("%lld\n",1ll*Mink1*Maxk2);return ;
	}
	if(Maxk1>=0&&Mink1>=0&&Maxk2<0&&Mink2<0){
		printf("%lld\n",1ll*Mink1*Mink2);return ;
	}
	if(Maxk1>=0&&Mink1>=0&&Maxk2>=0&&Mink2<0){
		printf("%lld\n",1ll*Mink1*Mink2);return ;
	}
	if(Maxk1>=0&&Mink1>=0&&Maxk2>=0&&Mink2>=0){
		printf("%lld\n",1ll*Maxk1*Mink2);return ;
	}
	if(Maxk1>=0&&Mink1<0&&Maxk2>=0&&Mink2>=0){
		printf("%lld\n",1ll*Maxk1*Mink2);return ;
	}
	if(Maxk1>=0&&Mink1<0&&Maxk2<0&&Mink2<0){
		printf("%lld\n",1ll*Mink1*Maxk2);return ;
	}
	if(Maxk1>=0&&Mink1<0&&Maxk2>=0&&Mink2<0){
		bool now=TA.ze(1,l1,r1) ;
		if(now)printf("0\n");
		else{
			int t1=TA.pre(1,l1,r1),t2=TA.next(1,l1,r1);
			long long res1=-inf,res2=-inf;
			if(t1!=-INF)res1=1ll*t1*Maxk2;
			if(t2!=INF)res2=1ll*t2*Mink2;		
			printf("%lld\n",max(res1,res2));
		}
	}
}
int main(){
    freopen("game.in","r",stdin);
    freopen("game.out","w",stdout);
	scanf("%d%d%d",&n,&m,&q);
	for(int i=1;i<=n;i++)scanf("%d",&A[i]);
	for(int i=1;i<=m;i++)scanf("%d",&B[i]);
	TA.build(1,1,n,A);
	TB.build(1,1,m,B);
	for(int i=1;i<=q;i++){
		int s1=0,s2=0,s3=0,s4=0;
		scanf("%d%d%d%d",&s1,&s2,&s3,&s4);
		work(s1,s2,s3,s4);
	}
	return 0;
}

\(T3\;\;星战\)

这题的暴力就是按题意模拟就行了,不太好打,但也能拿到分的,但我没拿,我大悲。

关于此题目前的正解,我只想说不愧是人类智慧题。

我们考虑反攻的条件,我们发现第二个条件是第一个条件的充要条件,所以说我们只要满足第二个条件就可以输出 \(Yes\),反之输出 \(No\)。我们发现在四种操作中,对于单条边的操作很好处理,复杂度为 \(O(1)\),但对于点的操作很难处理,一不小心就会达到 \(O(n)\) 的复杂度,我们考虑如何用人类智慧去处理这道题。

我们对于每个点随机一个权值 \(a_i\),我们再设每条当前存在的边的边权为其起点的点权 \(b_i\),那么我们满足条件的充要条件为:当前存在于场上的边有 \(n\) 条,并且 \(\sum_{i=1}^{n}b_i=\sum_{i=1}^{n}a_i\),这里的 \(\sum\) 可以是异或,也可以是普通的和,甚至一些满足结合律的其他操作也可以,那么我们就可以通过预处理,使得我们的所有操作的复杂度均为 \(O(1)\) 了。当然,因为我们用到了随机化,所以我们可以对每个点多随机几个权值,当每部分都满足条件时,我们才认为他满足条件,我们也就在一定程度上保证了正确性,减少了由随机所带来的误差。

用异或写的 \(code\),借用了 \(sandom\) 的名字,我很抱歉。

#include<bits/stdc++.h>
using namespace std;
const int N=5e5+10;
mt19937 sandom(time(NULL));
unsigned long long get(unsigned long long l,unsigned long long r){return 1llu*(1llu*sandom()*sandom()*sandom()*sandom()*sandom()*sandom()*sandom()*sandom()*sandom()+sandom())%(r-l+1)+l;}
int n,m,Q;
unsigned long long a[N][4],cha;
unsigned long long ans[4],sum[4];
unsigned long long d[N][4],nowd[N][4];
int deg[N],nowdeg[N];
int now;
int main(){
    freopen("galaxy.in","r",stdin);
    freopen("galaxy.out","w",stdout);
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++){
        for(int j=0;j<4;j++){
            a[i][j]=get(1llu,1000000000000000000llu);
            ans[j]^=a[i][j];
        }
    }
    for(int i=1;i<=m;i++){
        int s1=0,s2=0;
        scanf("%d%d",&s1,&s2);
        for(int j=0;j<4;j++)sum[j]^=a[s1][j],d[s2][j]^=a[s1][j],nowd[s2][j]^=a[s1][j];
        nowdeg[s2]++,deg[s2]++;
    }
    now=m;
    scanf("%d",&Q);
    for(int i=1;i<=Q;i++){
        int opt=0,s1=0,s2=0;
        scanf("%d",&opt);
        if(opt==1){
            scanf("%d%d",&s1,&s2);
            for(int j=0;j<4;j++)sum[j]^=a[s1][j],nowd[s2][j]^=a[s1][j];
            now--;
            nowdeg[s2]--;
        }
        else if(opt==2){
            scanf("%d",&s1);
            for(int j=0;j<4;j++)sum[j]^=nowd[s1][j],nowd[s1][j]=0;
            now-=nowdeg[s1];
            nowdeg[s1]=0;
        }
        else if(opt==3){
            scanf("%d%d",&s1,&s2);
            for(int j=0;j<4;j++)sum[j]^=a[s1][j],nowd[s2][j]^=a[s1][j];
            now++;
            nowdeg[s2]++;
        }
        else {
            scanf("%d",&s1);
            for(int j=0;j<4;j++)sum[j]^=nowd[s1][j]^d[s1][j],nowd[s1][j]=d[s1][j];
            now+=deg[s1]-nowdeg[s1];
            nowdeg[s1]=deg[s1];
        }
        printf(now==n&&sum[0]==ans[0]&&sum[1]==ans[1]&&sum[2]==ans[2]&&sum[3]==ans[3]?"YES\n":"NO\n");
    }
    return 0;
}

\(T4\;\;数据传输\)

我们发现 \(k=1\) 的情况就是两点之间的点权和,并且我们发现对于 \(k=2\) 的情况,我们只会在 \(s\to t\) 的这条链上走,因为我们如果不想走一个点的父亲,与其走到他父亲的另一个儿子那里,我们不如直接走到他父亲的父亲那里(因为我们即使走到了他父亲的另一个儿子那,我们不想走父亲,就还得走到他父亲的父亲,因为多走一个点肯定不优,那么我们直接走到他父亲的父亲更优)。

那么我们设 \(f_i\) 为从 \(s\to i\) 的最小答案,那么我们就可以单次 \(O(n)\) 递推了。

对于 \(k=3\) 的情况,我们是可以走到链外再走回来的,所以我们发现这种情况很不好搞,但由于 \(k=3\),所以我们多走的节点只可能是链上的点及其邻接点,所以我们将 \(DP\) 加一个维度。

我们设 \(f_{i,j}(j\in[0,2])\) 为从 \(s\to i\) ,当前位置在距离 \(i\)\(j\) 的地方的最小权值和,我们同时进行预处理,对于每个节点定义 \(a_{i,j}(j\in[0,1])\),其中 \(a_{i,0}\) 即为点权 \(v_i\)\(a_{i,1}\) 即为与 \(i\) 相接的点中最小的点权,那么我们就可发现转移式子了

\[ \begin{aligned} & [k=2] \begin{cases} & f_{next,0}=min(f_{i,0}+a_{next,0},f_{i,1}+a_{next,0}).\\ & f_{next,1}=f_{i,0}\\ \end{cases}\\ & [k=3] \begin{cases} & f_{next,0}=min(f_{i,0}+a_{next,0},f_{i,1}+a_{next,0},f_{i,2}+a_{next,0}).\\ & f_{next,1}=min(f_{i,0},f_{i,1}+a_{next,1}).\\ & f_{next,2}=f_{i,1}\\ \end{cases} \end{aligned} \]

解释一下 \(DP\) 转移。假设我们当前是向上转移。对于 \(k=2\) 的转移是显然的,对于 \(k=3\) 的转移,第一个是显然的,第二个是因为我们与 \(fa\) 贡献最小临接点可能为 \(i\),也可能为 \(i\) 的其他儿子,第三个和 \(k=2\) 的最后一个转移一样,因为当从距离 \(i\)\(0\) 的点转移到 \(fa\) 时,一定不如直接走到他父亲的父亲优,而且如果 \(f_{i,1}\) 很小,那么他可能在第一步时被作为答案,所以也不会对答案造成影响。

我们发现即使我们推出了状态转移方程,我们的单次复杂度还是最坏 \(O(n)\) 的,所以我们考虑矩阵优化。

我们首先使用广义的矩阵乘法,即设

\(A\times B=C,C_{i,j}=\min_{k=1}^{n} A_{i,k}+B_{k,j}\)

那么我们有

\[\begin{aligned} & [k=1]\\ & \begin{bmatrix} & f_{i,0} & f_{i,1} & f_{i,2} & \end{bmatrix} & * & \begin{bmatrix} & a_{next,0} & INF & INF\\ & INF & INF & INF\\ & INF & INF & INF & \end{bmatrix} & = & \begin{bmatrix} & f_{next,0} & f_{next,1} & f_{next,2} & \end{bmatrix} \end{aligned} \]

\[\begin{aligned} & [k=2]\\ & \begin{bmatrix} & f_{i,0} & f_{i,1} & f_{i,2} & \end{bmatrix} & * & \begin{bmatrix} & a_{next,0} & 0 & INF\\ & a_{next,0} & INF & INF\\ & INF & INF & INF & \end{bmatrix} & = & \begin{bmatrix} & f_{next,0} & f_{next,1} & f_{next,2} & \end{bmatrix} \end{aligned} \]

\[\begin{aligned} & [k=3]\\ & \begin{bmatrix} & f_{i,0} & f_{i,1} & f_{i,2} & \end{bmatrix} & * & \begin{bmatrix} & a_{next,0} & 0 & INF\\ & a_{next,0} & a_{next,1} & 0\\ & a_{next,0} & INF & INF & \end{bmatrix} & = & \begin{bmatrix} & f_{next,0} & f_{next,1} & f_{next,2} & \end{bmatrix} \end{aligned} \]

特别的,我们在 \(s\) 处的初始矩阵为

\[ \begin{bmatrix} a_{s,0} & INF & INF\\ INF & INF & INF\\ INF & INF & INF \end{bmatrix} \]

我们就可以通过倍增或树剖或者其他数据结构来维护矩阵乘,这样复杂度就由单次 \(O(n)\) 变为了单次 \(O(C^3 logn)\)\(O(C^3 log^2n)\)\(C\) 为矩阵大小,单 \(log\) 的是倍增,双 \(log\) 的为树剖)。

所以我们的最终复杂度即为 \(O(QC^3log^2n)\)\(O(QC^3logn)\)

我的树剖 \(O(QC^3log^2n)\)\(code\)

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5;
long long a[N][2];
int n,Q,k;
struct Graph{
    int head[N],tot,dep[N],fa[N],top[N],siz[N],son[N],dfn[N],dian[N],ntime;
    struct Node{int to,nest;}bian[N<<1];
    void add(int x,int y){bian[++tot]=(Node){y,head[x]};head[x]=tot;}
    void get_heavy_son(int x,int f,int depth){
        siz[x]=1,fa[x]=f,son[x]=0,dep[x]=depth;
        for(int i=head[x];i;i=bian[i].nest){
            int v=bian[i].to;
            if(v==f)continue;
            get_heavy_son(v,x,depth+1);
            siz[x]+=siz[v];
            if(siz[v]>siz[son[x]])son[x]=v;
        }
    }
    void get_heavy_edge(int x,int tp){
        top[x]=tp,dfn[x]=++ntime,dian[ntime]=x;
        if(son[x])get_heavy_edge(son[x],tp);
        for(int i=head[x];i;i=bian[i].nest){
            int v=bian[i].to;
            if(v==fa[x]||v==son[x])continue;
            get_heavy_edge(v,v);
        }
    }
    int lca(int x,int y){
        while(top[x]!=top[y]){
            if(dep[top[x]]<dep[top[y]])swap(x,y);
            x=fa[top[x]];
        }
        return dep[x]<dep[y]?x:y;
    }
}G;
struct Matrix{
    long long a[3][3];
    Matrix(){memset(a,0x3f,sizeof(a));}
    friend Matrix operator *(Matrix x,Matrix y){
        Matrix z;
        for(int i=0;i<3;i++){
            for(int j=0;j<3;j++){
                for(int k=0;k<3;k++){
                    z.a[i][j]=min(z.a[i][j],x.a[i][k]+y.a[k][j]);
                }
            }
        }
        return z;
    }
}I;
Matrix Begin(int x){
    Matrix z;
    z.a[0][0]=a[x][0];
    return z;
}
Matrix A(int x){
    Matrix z;
    if(k==1)z.a[0][0]=a[x][0];
    else if(k==2)z.a[0][0]=a[x][0],z.a[0][1]=0,z.a[1][0]=a[x][0];
    else z.a[0][0]=z.a[1][0]=z.a[2][0]=a[x][0],z.a[0][1]=z.a[1][2]=0,z.a[1][1]=a[x][1];
    return z;
}
struct Segment_Tree{
    #define lson (rt << 1)
    #define rson (rt << 1 | 1)
    struct Seg{Matrix tt,rtt;}tree[N<<2];
    void pushup(int rt){
        tree[rt].tt=tree[lson].tt*tree[rson].tt;
        tree[rt].rtt=tree[rson].rtt*tree[lson].rtt;
    }
    void build(int rt,int l,int r){
        if(l==r){
            tree[rt].tt=tree[rt].rtt=A(G.dian[l]);
            return ;
        }
        int mid=(l+r)>>1;
        build(lson,l,mid);
        build(rson,mid+1,r);
        pushup(rt);
    }
    Matrix ask(int rt,int l,int r,int L,int R,int opt){
        if(l>r) return I;
        if(l<=L&&R<=r)return ((opt==1)?tree[rt].tt:tree[rt].rtt);
        int mid=(L+R)>>1;
        Matrix tmp1,tmp2;
        tmp1=I,tmp2=I;
        if(l<=mid)tmp1=ask(lson,l,r,L,mid,opt);
        if(r>mid)tmp2=ask(rson,l,r,mid+1,R,opt);
        if(opt==1)return tmp1*tmp2;
        else return tmp2*tmp1;
    }
}T;
Matrix getans(int x,int y){
    Matrix res1,res2;
    res1=I,res2=I;
    int last=x;
    while(G.top[x]!=G.top[y]){
        if(G.dep[G.top[x]]<G.dep[G.top[y]]){
            res2=T.ask(1,G.dfn[G.top[y]],G.dfn[y],1,G.ntime,1)*res2;
            y=G.fa[G.top[y]];
        }
        else{
            if(x==last)res1=res1*T.ask(1,G.dfn[G.top[x]],G.dfn[G.fa[x]],1,G.ntime,2);
            else res1=res1*T.ask(1,G.dfn[G.top[x]],G.dfn[x],1,G.ntime,2);
            x=G.fa[G.top[x]];
        }
    }
    if(G.dep[x]<G.dep[y]){
        if(x==last)res2=T.ask(1,G.dfn[G.son[x]],G.dfn[y],1,G.ntime,1)*res2;
        else res2=T.ask(1,G.dfn[x],G.dfn[y],1,G.ntime,1)*res2;
    }
    else{
        if(x==last)res1=res1*T.ask(1,G.dfn[y],G.dfn[G.fa[x]],1,G.ntime,2);
        else res1=res1*T.ask(1,G.dfn[y],G.dfn[x],1,G.ntime,2);
    }
    return res1*res2;
}
long long ask(int s,int t){
    Matrix as;as=Begin(s);
    Matrix tmp;tmp=getans(s,t);
    return (as*tmp).a[0][0];
}
int main(){
    freopen("transmit.in","r",stdin);
    freopen("transmit.out","w",stdout);
    memset(a,0x3f,sizeof(a));
    scanf("%d%d%d",&n,&Q,&k);
    for(int i=0;i<3;i++)I.a[i][i]=0;
    for(int i=1;i<=n;i++)scanf("%lld",&a[i][0]);
    for(int i=1;i<=n-1;i++){
        int s1=0,s2=0;
        scanf("%d%d",&s1,&s2);
        G.add(s1,s2);
        G.add(s2,s1);
    }
    for(int i=1;i<=n;i++){
        for(int j=G.head[i];j;j=G.bian[j].nest){
            int v=G.bian[j].to;
            a[i][1]=min(a[i][1],a[v][0]);
        }
    }
    G.get_heavy_son(1,0,1);
    G.get_heavy_edge(1,1);
    T.build(1,1,G.ntime);
    for(int i=1;i<=Q;i++){
        int s1=0,s2=0;
        scanf("%d%d",&s1,&s2);
        printf("%lld\n",ask(s1,s2));
    }
    return 0;
}

总之,这次 \(CSP-S\) 已经结束了,接下来的任务是 \(NOIP\) 了,祝愿 \(HZOI\) \(NOIP\) \(RP++\)

\(update:\) 成绩出来了 \(175\),还算不幸中的万幸,\(CCF\) 没卡我常数。

posted @ 2022-11-05 20:47  hxqasd  阅读(80)  评论(1编辑  收藏  举报