prow2012-A3-0019

从 Trac 迁移的文章

这是从旧校内 Wiki 迁移的文章,可能存在一些样式问题,您可以向 memset0 反馈。

原文章内容如下:

{{{
这题是改编自{{{http://poj.org/problem?id=3728}}}
题意:给出一棵节点有值的树,给出Q个询问(a,b),问从a到b的最大盈利
(即:先在最小值买入,再在最大值卖出)

比赛时没发现,现在看懂题意后觉得好眼熟。。。原来在POJ做过类似题,这题只不过多求一个MST。

求好了一棵生成树以后,就是类似一个树形DP的并查集操作(边tarjan求LCA边算答案)

要求出a->b的答案,假设lca(a,b)= fa 
需要得到的是 
a->fa路径上的数值最大值 fmax(a) 
fa->b路径上的数值最小值 fmin(b) 
a->fa路径上的最大盈利(答案) ffrom(a) 
fa->b路径上的最大盈利(答案) fto(b) 
得到这些量后,a->b的答案就等于 max(ffrom(a),fto(b),fmax(a)-fmin(b)) 

如何维护得到这四个量:
{{{
int findroot(int x) {
    if (father[x] == x) return x;
    int fa = father[x];
    father[x] = findroot(father[x]);
    ffrom[x] = max(ffrom[x], ffrom[fa]);
    ffrom[x] = max(ffrom[x], fmax[fa] - fmin[x]);
    fto[x] = max(fto[fa], fto[x]);
    fto[x] = max(fto[x], fmax[x] - fmin[fa]);
    fmax[x] = max(fmax[fa], fmax[x]);
    fmin[x] = min(fmin[fa], fmin[x]);
    return father[x];
}
}}}
在并查集的同时,fa记录下当前的父亲,对fa进行findroot(这样就维护好了fa的正确dp值) 
然后从fa的值DP到当前点x的dp值。维护完毕。


在查询时,需要深刻理解LCA的tarjan思想,而不是会用模板——因为你得知道 
对于a->b的查询,当lca到b时,a的父亲是lca(a,b),而b的父亲不是! 

所以对于b->lca(a,b)这一段的dp值,不能通过ffrom[],fto[],fmax,fmin去读取 
而是要一直追溯上去到lca(a,b)为止求得这一段的dp值,无需记录 

}}}

{{{

/* 
 * File:   main.cpp
 * Author: prowindy
 *
 * Created on 2010年11月10日, 下午13:17
 */

#include <cstdlib>
#include <vector>
#include <iostream>
#include <string.h>
#include <stdio.h>
#include <algorithm>
using namespace std;
int node[200000], next[200000], edge[100000], tot;
int father[200000], fmax[200000], fmin[200000], Num[200000];
bool lcavis[100000], visit[100000];
int Res[200000];
int ffrom[200000], fto[200000], tfa[200000];
int X[100000],Y[100000],order[100000],cost[100000];
void insert(int a, int b) {
    node[tot] = b, next[tot] = edge[a], edge[a] = tot++;
}
#define PII pair<int,int>
vector<PII> Query[100000];

void initset(int N) {
    for (int i = 0; i <= N; i++) {
        father[i] = i;
        Query[i].clear();
        fmax[i] = fmin[i] = Num[i];
        ffrom[i] = fto[i] = 0;
    }
}

int findroot(int x) {
    if (father[x] == x) return x;
    int fa = father[x];
    father[x] = findroot(father[x]);
    ffrom[x] = max(ffrom[x], ffrom[fa]);
    ffrom[x] = max(ffrom[x], fmax[fa] - fmin[x]);
    fto[x] = max(fto[fa], fto[x]);
    fto[x] = max(fto[x], fmax[x] - fmin[fa]);
    fmax[x] = max(fmax[fa], fmax[x]);
    fmin[x] = min(fmin[fa], fmin[x]);
    return father[x];
}

void Unoin(int a, int b) {
    father[a] = b;
    return;
}
int t, u, i, fa, now, ans, from, to, Max, Min;

void calc(int v) {
    t = Query[v].size();
    for (i = 0; i < t; i++) {
        u = Query[v][i].first;
        if (!visit[u]) continue;
        fa = findroot(u);

        if (Query[v][i].second < 0) {
            now = v;
            to = 0, Max = Num[now];
            while (now != fa&&now!=-1) {
                to = max(to, Max - Num[tfa[now]]);
                Max = max(Max, Num[tfa[now]]);
                now = tfa[now];
            }
            if (now==-1) ans = -1;
            else {
            ans = max(ffrom[u], to);
            ans = max(ans, Max - fmin[u]);
            }
            Res[abs(Query[v][i].second)] = ans;

        } else {
            now = v;
            from = 0, Min = Num[now];
            while (now != fa&&now!=-1) {
                from = max(from, Num[tfa[now]] - Min);
                Min = min(Min, Num[tfa[now]]);
                now = tfa[now];
            }
            if (now==-1) ans = -1;
            else {
            ans = max(fto[u], from);
            ans = max(ans, fmax[u] - Min);
            }
            Res[abs(Query[v][i].second)] = ans;
        }
    }
}

void LCA(int v) {
    lcavis[v] = true;
    for (int i = edge[v]; i != -1; i = next[i])
        if (!lcavis[node[i]]) {
            tfa[node[i]] = v;
            LCA(node[i]);
            Unoin(node[i], v);
        }
    visit[v] = true;
    calc(v);
}

/*
 * 
 */
bool cmp(int i,int j){
    return cost[i]<cost[j];
}
int main(int argc, char** argv) {
    int N, i, a, b, M;
while (scanf("%d", &N) != EOF) {

    memset(edge, -1, sizeof (edge));
    memset(visit, false, sizeof (visit));
    memset(lcavis, false, sizeof (lcavis));
    tot = 0;
    for (i = 1; i <= N; i++)
        scanf("%d", &Num[i]);
    initset(N);
    scanf("%d",&M);
    for (i=0;i<M;i++){
        scanf("%d%d%d",&X[i],&Y[i],&cost[i]);
        cost[i] *=-1;
        order[i] = i;
    }
    sort(order,order+M,cmp);
    int cnt = 0;
    long long ans1 = 0;
    for (i=0;i<M;i++){
        int a = findroot(X[order[i]]),b = findroot(Y[order[i]]);
        if (a!=b){
            father[a] = b;
            cnt++;
            ans1-=cost[order[i]];
            insert(X[order[i]],Y[order[i]]);
            insert(Y[order[i]],X[order[i]]);
            //printf("%d %d\n",X[order[i]],Y[order[i]]);
        }
        if (cnt==N-1) break;
    }
//  if (i==M) while(1);
    initset(N);
    scanf("%d", &M);
    for (i = 1; i <= M; i++) {
        scanf("%d%d", &a, &b);
        Query[a].push_back(make_pair(b, i));
        Query[b].push_back(make_pair(a, -i));
        Res[i] = -1;
    }
    for (i=1;i<=N;i++)
        if (!lcavis[i])
            tfa[i] = -1,LCA(i);
    //printf("%d\n",ans1);
    cout<<ans1<<endl;
    for (i = 1; i <= M; i++)
        printf("%d\n", Res[i]);

    }
    return 0;
}


}}}
这题是改编自{{{http://poj.org/problem?id=3728}}}
题意:给出一棵节点有值的树,给出Q个询问(a,b),问从a到b的最大盈利
(即:先在最小值买入,再在最大值卖出)
比赛时没发现,现在看懂题意后觉得好眼熟。。。原来在POJ做过类似题,这题只不过多求一个MST。
求好了一棵生成树以后,就是类似一个树形DP的并查集操作(边tarjan求LCA边算答案)
要求出a->b的答案,假设lca(a,b)= fa 
需要得到的是 
a->fa路径上的数值最大值 fmax(a) 
fa->b路径上的数值最小值 fmin(b) 
a->fa路径上的最大盈利(答案) ffrom(a) 
fa->b路径上的最大盈利(答案) fto(b) 
得到这些量后,a->b的答案就等于 max(ffrom(a),fto(b),fmax(a)-fmin(b)) 
如何维护得到这四个量:
{{{
int findroot(int x) {
    if (father[x] == x) return x;
    int fa = father[x];
    father[x] = findroot(father[x]);
    ffrom[x] = max(ffrom[x], ffrom[fa]);
    ffrom[x] = max(ffrom[x], fmax[fa] - fmin[x]);
    fto[x] = max(fto[fa], fto[x]);
    fto[x] = max(fto[x], fmax[x] - fmin[fa]);
    fmax[x] = max(fmax[fa], fmax[x]);
    fmin[x] = min(fmin[fa], fmin[x]);
    return father[x];
}
}}}
在并查集的同时,fa记录下当前的父亲,对fa进行findroot(这样就维护好了fa的正确dp值) 
然后从fa的值DP到当前点x的dp值。维护完毕。
在查询时,需要深刻理解LCA的tarjan思想,而不是会用模板——因为你得知道 
对于a->b的查询,当lca到b时,a的父亲是lca(a,b),而b的父亲不是! 
所以对于b->lca(a,b)这一段的dp值,不能通过ffrom[],fto[],fmax,fmin去读取 
而是要一直追溯上去到lca(a,b)为止求得这一段的dp值,无需记录 
/* 
 * File:   main.cpp
 * Author: prowindy
 *
 * Created on 2010年11月10日, 下午13:17
 */
#include <cstdlib>
#include <vector>
#include <iostream>
#include <string.h>
#include <stdio.h>
#include <algorithm>
using namespace std;
int node[200000], next[200000], edge[100000], tot;
int father[200000], fmax[200000], fmin[200000], Num[200000];
bool lcavis[100000], visit[100000];
int Res[200000];
int ffrom[200000], fto[200000], tfa[200000];
int X[100000],Y[100000],order[100000],cost[100000];
void insert(int a, int b) {
    node[tot] = b, next[tot] = edge[a], edge[a] = tot++;
}
#define PII pair<int,int>
vector<PII> Query[100000];
void initset(int N) {
    for (int i = 0; i <= N; i++) {
        father[i] = i;
        Query[i].clear();
        fmax[i] = fmin[i] = Num[i];
        ffrom[i] = fto[i] = 0;
    }
}
int findroot(int x) {
    if (father[x] == x) return x;
    int fa = father[x];
    father[x] = findroot(father[x]);
    ffrom[x] = max(ffrom[x], ffrom[fa]);
    ffrom[x] = max(ffrom[x], fmax[fa] - fmin[x]);
    fto[x] = max(fto[fa], fto[x]);
    fto[x] = max(fto[x], fmax[x] - fmin[fa]);
    fmax[x] = max(fmax[fa], fmax[x]);
    fmin[x] = min(fmin[fa], fmin[x]);
    return father[x];
}
void Unoin(int a, int b) {
    father[a] = b;
    return;
}
int t, u, i, fa, now, ans, from, to, Max, Min;
void calc(int v) {
    t = Query[v].size();
    for (i = 0; i < t; i++) {
        u = Query[v][i].first;
        if (!visit[u]) continue;
        fa = findroot(u);
        if (Query[v][i].second < 0) {
            now = v;
            to = 0, Max = Num[now];
            while (now != fa&&now!=-1) {
                to = max(to, Max - Num[tfa[now]]);
                Max = max(Max, Num[tfa[now]]);
                now = tfa[now];
            }
            if (now==-1) ans = -1;
            else {
            ans = max(ffrom[u], to);
            ans = max(ans, Max - fmin[u]);
            }
            Res[abs(Query[v][i].second)] = ans;
        } else {
            now = v;
            from = 0, Min = Num[now];
            while (now != fa&&now!=-1) {
                from = max(from, Num[tfa[now]] - Min);
                Min = min(Min, Num[tfa[now]]);
                now = tfa[now];
            }
            if (now==-1) ans = -1;
            else {
            ans = max(fto[u], from);
            ans = max(ans, fmax[u] - Min);
            }
            Res[abs(Query[v][i].second)] = ans;
        }
    }
}
void LCA(int v) {
    lcavis[v] = true;
    for (int i = edge[v]; i != -1; i = next[i])
        if (!lcavis[node[i]]) {
            tfa[node[i]] = v;
            LCA(node[i]);
            Unoin(node[i], v);
        }
    visit[v] = true;
    calc(v);
}
/*
 * 
 */
bool cmp(int i,int j){
    return cost[i]<cost[j];
}
int main(int argc, char** argv) {
    int N, i, a, b, M;
while (scanf("%d", &N) != EOF) {
    memset(edge, -1, sizeof (edge));
    memset(visit, false, sizeof (visit));
    memset(lcavis, false, sizeof (lcavis));
    tot = 0;
    for (i = 1; i <= N; i++)
        scanf("%d", &Num[i]);
    initset(N);
    scanf("%d",&M);
    for (i=0;i<M;i++){
        scanf("%d%d%d",&X[i],&Y[i],&cost[i]);
        cost[i] *=-1;
        order[i] = i;
    }
    sort(order,order+M,cmp);
    int cnt = 0;
    long long ans1 = 0;
    for (i=0;i<M;i++){
        int a = findroot(X[order[i]]),b = findroot(Y[order[i]]);
        if (a!=b){
            father[a] = b;
            cnt++;
            ans1-=cost[order[i]];
            insert(X[order[i]],Y[order[i]]);
            insert(Y[order[i]],X[order[i]]);
            //printf("%d %d\n",X[order[i]],Y[order[i]]);
        }
        if (cnt==N-1) break;
    }
//  if (i==M) while(1);
    initset(N);
    scanf("%d", &M);
    for (i = 1; i <= M; i++) {
        scanf("%d%d", &a, &b);
        Query[a].push_back(make_pair(b, i));
        Query[b].push_back(make_pair(a, -i));
        Res[i] = -1;
    }
    for (i=1;i<=N;i++)
        if (!lcavis[i])
            tfa[i] = -1,LCA(i);
    //printf("%d\n",ans1);
    cout<<ans1<<endl;
    for (i = 1; i <= M; i++)
        printf("%d\n", Res[i]);
    }
    return 0;
}