【JZOJ5262】【GDOI2018模擬8.12】樹(DP,性質題)

Description

這裏寫圖片描述

Solution

首先我們可以知道兩個性質:1、路徑u-v和路徑v-w可以合併爲路徑u-w;2、路徑u1-v1加路徑u2-v2和路徑u1-v2加路徑u2-v1是等價的(就是起始點和終點可以互換)
那麼知道這些性質之後就很好做了。我們只用知道每個點多少次做起點和多少次做終點。
我們設f[i]表示滿足i子樹的需求i上的值要是多少。
那麼枚舉i的所有兒子,判斷a[i]-f[i],看看當前是需要增大還是需要變小,然後再看看哪一個的編號大,判斷路徑方向,然後更新in和out(表示出度和入度)
然後起點終點去個min減一下就知道每個點做起點或終點多少次了,然後把所有的起點和終點排一次序,兩兩連接就好了。

Code

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<math.h>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fod(i,a,b) for(i=a;i>=b;i--)
#define rep(i,a) for(i=first[a];i;i=next[i])
using namespace std;
const int maxn=1e6+7;
int i,j,k,l,t,n,m,ans,head,tail,x,y,z,fu;
int first[maxn*2],last[maxn*2],next[maxn*2],num,a[maxn];
int f[maxn],d[maxn],fa[maxn],in[maxn],out[maxn];
int b[maxn],c[maxn];
void add(int x,int y){
    last[++num]=y,next[num]=first[x],first[x]=num;
}
int main(){
//  freopen("fan.in","r",stdin);
//  freopen("fan.out","w",stdout);
    scanf("%d",&n);
    fo(i,1,n)scanf("%d",&a[i]);
    fo(i,1,n-1)scanf("%d%d",&k,&l),add(k,l),add(l,k);
    head=0,d[tail=1]=1;
    while(head<tail){
        rep(i,d[++head]){
            if(last[i]==fa[d[head]])continue;
            fa[last[i]]=d[head];d[++tail]=last[i];
        }
    }
    fod(k,n,1){
        x=d[k];
        rep(i,x){
            y=last[i];if(y==fa[x])continue;
            z=a[y]-f[y];
            if(z>0){
                if(x>y)in[x]+=z,out[y]+=z,f[x]+=z;
                else out[x]+=z,in[y]+=z,f[x]+=z;
            }
            else{
                z=-z;
                if(x>y)in[y]+=z,out[x]+=z,f[x]-=z;
                else out[y]+=z,in[x]+=z,f[x]-=z;
            }
        }
    }
    fo(i,1,n){
        j=min(in[i],out[i]),in[i]-=j,out[i]-=j;
        while(in[i])in[i]--,c[++c[0]]=i;
        while(out[i])out[i]--,b[++b[0]]=i;
    }
    printf("%d\n",b[0]);
    fo(i,1,b[0])printf("%d %d\n",b[i],c[i]);
}
發佈了433 篇原創文章 · 獲贊 631 · 訪問量 37萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章