【JZOJ5262】【GDOI2018模拟8.12】树

Description

这里写图片描述

Data Constraint

这里写图片描述

Solution

我们发现两个显而易见(一点都不显然好吗)的结论:
1、是否优先不论,我们发现一条u->v的路径可以拆分成u->lca,lca->v两条路径,反之也成立。
2、a->b和c->d等价于a->d和c->b
于是我们得到推论:一个点要么作为起点要么作为终点。(吼啊啊啊)
那么我们就可以很开心的做dp了,由于叶子节点必须靠父亲边才能往上走,所以我们从叶子做起。设f(i)表示i在满足除i以外的以i为根的子树所有的点权值后i的权值,那么若f(i)≠a[i]这锅肯定要靠i与i的父亲的那条边来背,所以f(fa[i])+=a[i]-f(i),表示以i为根的子树要有多少条边经过i的父亲边。同时我们将数量加入i,fa[i]的出入边中。最后统计一下每个点是出边多还是入边多,将它放至边较多的一方。由于结论2,我们可以分别将作为起点和终点的点按大小排序,最后从小往大的匹配即可。

Code

#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=2e6+5;
struct code{
    int x,sum;
}b[maxn],c[maxn];
int f[maxn],g[maxn],a[maxn],first[maxn],last[maxn],next[maxn],v[maxn],bz[maxn],fa[maxn];
int n,m,i,t,j,k,l,x,y,z,num,num1;
char ch;
int get(){
    char ch=getchar();int x=0,z=1;
    if (ch=='-') z=-1,ch=getchar();
    while (ch>=48 && ch<=57) x=x*10+ch-48,ch=getchar();
    return x*z;
}
void lian(int x,int y){
    last[++num]=y;next[num]=first[x];first[x]=num;
}
void bfs(){
    int i=0,j=1;v[1]=1;
    while (i<j){
        x=v[++i];bz[x]=1;
        for (t=first[x];t;t=next[t])
            if (!bz[last[t]])v[++j]=last[t],fa[v[j]]=x;
    }
}
bool cmp(code x,code y){
    return x.x<y.x;
}
void put(int x){
    if (x<10){
        ch=x+48;
        putchar(ch);return;
    }
    put(x/10);ch=x%10+48;putchar(ch);
}
int main(){
//  freopen("data.in","r",stdin);freopen("data.out","w",stdout);
    scanf("%d\n",&n);
    for (i=1;i<=n;i++)a[i]=get();
    for (i=1;i<n;i++)x=get(),y=get(),lian(x,y),lian(y,x);
    bfs();num=num1=0;
    for (i=n;i>=1;i--){
        x=v[i];t=0;
        for (t=first[x];t;t=next[t]){
            if (last[t]==fa[x]) continue;
            f[x]+=a[last[t]]-f[last[t]];
            if (last[t]>x) z=1;
            else z=-1;
            g[last[t]]+=z*(a[last[t]]-f[last[t]]);
            g[x]+=-z*(a[last[t]]-f[last[t]]);
        }
    }t=0;
    memset(bz,0,sizeof(bz));
    for (i=1;i<=n;i++)
        if (g[i]>0) t+=g[i],bz[i]=1;
    for (i=1;i<=n;i++)
        if (bz[i]) b[++num].x=i,b[num].sum=g[i],bz[i]=0;
    for (i=1;i<=n;i++)
        if (g[i]<0) bz[i]=1;
    for (i=1;i<=n;i++)
        if (bz[i]) c[++num1].x=i,c[num1].sum=-g[i];
    k=1;
    printf("%d\n",t);
    for (i=1;i<=num1;i++){
        for (j=1;j<=c[i].sum;j++){
            //printf("%d %d\n",c[i].x,b[k].x);
            put(c[i].x);putchar(' ');put(b[k].x);putchar('\n');
            b[k].sum--;
            if (!b[k].sum)k++;
        }
    }
}
发布了257 篇原创文章 · 获赞 451 · 访问量 10万+

猜你喜欢

转载自blog.csdn.net/crybymyself/article/details/77435434