【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++;
        }
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章