SkyWT / 博客 / ZOJ 3649 Social Net:最大生成树+树上倍增 DP

ZOJ 3649 Social Net:最大生成树+树上倍增 DP

2018 年 8 月 3 日 12:53


文章目录

题目链接:ZOJ 3649 Social Net

Problem

There are nn individuals (2<=n<=30000)(2 <= n <= 30000). Everyone has one or more friends. And everyone can contact all people by friend-relation. If two persons aren't friends, they also can contact by their friends. Each pair of friends have a friendship value aia_i (1<=ai<=50000)(1 <= a_i <= 50000).
Firstly, you will relieve some friend-relation. The rest of the friend-relation is the social net. The net is unique in all test cases. In this net, everyone can contact all people by rest friend-relation. The net has a minimum number of friend-relation. And the net has maximum sum of friendship value. We want to get the maximum sum.
Secondly, everyone has an angry value bibi (1<=bi<=100000)(1 <= b_i <= 100000). We have qq operations (1<=q<=30000)(1 <= q <= 30000): Person XX wants to contact person YY, this operation merely has one sequence which describes the process. The sequence consists of persons' angry value. The persons are on the process.
We suppose the sequence is c1,c2,c3,...,cic_1, c_2, c_3, ... ,c_i. Here cic_i means the angry value of the iith people in the sequence.
We attempt to find the maximum ckcjc_k-c_j (ck>=cj,j<=k)(c_k >= c_j, j <= k).

Example:
The sequence is 3(X), 4, 5, 6, 7, 5, 9, 4, 11(Y). The maximum ckcjc_k-c_j is 11-3=8.
The sequence is 3(X), 4, 5, 6, 7, 5, 9, 2, 11(Y). The maximum ckcjc_k-c_j is 11-2=9.
The sequence is 3(X), 10, 2, 5(Y). The maximum ckcjc_k-c_j is 10-3=7.

Input

The input contains multiple test cases. Each test case begins with a line containing a single integer nn. The following line contains nn integers bib_i.
The subsequent line describe the number of relations mm (n<=m<=50000)(n <= m <= 50000). The next mm lines contain the information about relations: xx, yy, aia_i. Their friendship value is aia_i.
Afterward gives qq. The next qq lines contain the operations: xx, yy. person XX wants to contact person YY.

Output

For each case, print maximum sum of friendship value of the net on the first line.
The next qq lines contain the answers of every operations.

Sample Input

6
3 5 1 7 3 5
7
1 2 5
1 3 6
2 4 7
2 5 8
3 6 9
4 5 1
5 6 2
5
6 1
6 2
6 3
6 4
6 5

Sample Output

35
2
4
0
6
4

Translation

题目意思抽象出来是:给你 nn 个点的一张图,先求最大生成树,然后在树里给出一大堆询问,对于每对询问给出两个参数 xi,yix_i,y_i,问你树上这两点之间最短路上 w(j)w(i)w(j)-w(i) 的最大值,其中要保证 i<=ji <= j

Analysis

第一问求最大生成树果断的 Kruskal,不用讲了……

第二问,如果直接问我们路径上 w(j)w(i)w(j)-w(i) 的最大值,很显然可以用树上倍增解决,但是麻烦之处就在于这个 iji \le j。这可怎么办呢?

我们可以用一个分治的思想:如果把 XXYY 的路径看成一个数列,那么我们可以从中间割开,左右分别处理出答案,然后最终答案就是这个数列里右边的最大值和左边的最小值之差,和左右答案取个 max。

那么我们自然想到了:维护五个树上倍增数组:

  • F(i,j)F(i,j) 表示从 ii 点向上推 2j2^j 个单位的点;

  • maxnum(i,j)max_num(i,j) 表示从 ii 点向上推 2j2^j 个单位的距离内 w(i)w(i) 的最大值;

  • minnum(i,j)min_num(i,j) 表示从 ii 点向上推 2j2^j 个单位的距离内 w(i)w(i) 的最小值;

  • difupd(i,j)dif_upd(i,j) 表示从 ii 点向上推 2j2^j 个单位的距离内的差值最大值;

  • difdwn(i,j)dif_dwn(i,j) 表示从 ii 点向上推 2j2^j 个单位的距离内的反向差值最大值;

什么叫“反向差值”呢?根据 iji \le j,“正向差值”是指对于 iji \le j 的差值,“反向差值” 则是对于 jij \le i 的差值。

其实思路清晰,写起来并不算麻烦。

Code

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=30005,maxm=60005,maxe=50005;
int n,m,q,w[maxn],fa[maxn],deep[maxn],sum=0;
int tot=0,lnk[maxn],nxt[maxm],son[maxm];
int f[maxn][20],max_num[maxn][20],min_num[maxn][20],dif_upd[maxn][20],dif_dwn[maxn][20];
bool vis[maxn];
struct Edge{
    int x,y,w;
}e[maxe];
inline int max3(int x,int y,int z){
    return z>max(x,y)?z:max(x,y);
}
inline bool Compare(Edge a,Edge b){
    return a.w>b.w;
}
inline int read(){
    int ret=0,f=1;char ch=getchar();
    while (ch<'0'||ch>'9') {if (ch=='-') f=-1;ch=getchar();}
    while (ch>='0'&&ch<='9') ret=ret*10+ch-'0',ch=getchar();
    return ret*f;
}
inline int Abs(int x){
    return x<0?-x:x;
}
inline void add(int x,int y){
    tot++;son[tot]=y;nxt[tot]=lnk[x];lnk[x]=tot;
}
inline void Init(){
    memset(w,0,sizeof(w));
    memset(fa,0,sizeof(fa));
    memset(deep,0,sizeof(deep));
    memset(f,0,sizeof(f));
    memset(max_num,0,sizeof(max_num));
    memset(min_num,0,sizeof(min_num));
    memset(dif_upd,0,sizeof(dif_upd));
    memset(dif_dwn,0,sizeof(dif_dwn));
    tot=0;sum=0;
    memset(lnk,0,sizeof(lnk));
    memset(nxt,0,sizeof(nxt));
    memset(son,0,sizeof(son));
    memset(vis,0,sizeof(vis));
    memset(e,0,sizeof(e));
}
inline int getfa(int x){
    if (fa[x]==x) return x;
    fa[x]=getfa(fa[x]);
    return fa[x];
}
inline void Kruskal(){
    sort(e+1,e+1+m,Compare);
    for (int i=1;i<=n;i++) fa[i]=i;
    int cnt=0;
    for (int i=1;i<=m&&cnt<n-1;i++){
        int fx=getfa(e[i].x),fy=getfa(e[i].y);
        if (fx==fy) continue;
        sum+=e[i].w;
        add(e[i].x,e[i].y);add(e[i].y,e[i].x);
        fa[fx]=fy;cnt++;
    }
    if (cnt!=n-1) printf("ERROR!!!!!\n");
}
inline void BuildTree(int x){
    vis[x]=1;
    for (int i=lnk[x];i;i=nxt[i]) if (!vis[son[i]]){
        fa[son[i]]=x;
        deep[son[i]]=deep[x]+1;
        BuildTree(son[i]);
    }
}
inline void Build(){
    for (int i=1;i<=n;i++){
        f[i][0]=fa[i];
        max_num[i][0]=max(w[i],w[fa[i]]);
        min_num[i][0]=min(w[i],w[fa[i]]);
        dif_upd[i][0]=w[fa[i]]-w[i];dif_dwn[i][0]=w[i]-w[fa[i]];
    }
    for (int j=1;j<20;j++)
        for (int i=1;i<=n;i++){
            f[i][j]=f[f[i][j-1]][j-1];
            max_num[i][j]=max(max_num[i][j-1],max_num[f[i][j-1]][j-1]);
            min_num[i][j]=min(min_num[i][j-1],min_num[f[i][j-1]][j-1]);
            dif_upd[i][j]=max3(dif_upd[i][j-1],dif_upd[f[i][j-1]][j-1],max_num[f[i][j-1]][j-1]-min_num[i][j-1]);
            dif_dwn[i][j]=max3(dif_dwn[i][j-1],dif_dwn[f[i][j-1]][j-1],max_num[i][j-1]-min_num[f[i][j-1]][j-1]);
        }
    // for (int i=1;i<=n;i++){
    //     printf("I=%d -------------\n",i);
    //     for (int j=0;j<19;j++) printf("%d ",f[i][j]);printf("\n");
    //     printf("MAX_NUM: ");for (int j=0;j<19;j++) printf("%d ",max_num[i][j]);printf("\n");
    //     printf("MIN_NUM: ");for (int j=0;j<19;j++) printf("%d ",min_num[i][j]);printf("\n");
    //     printf("DIF_UPD: ");for (int j=0;j<19;j++) printf("%d ",dif_upd[i][j]);printf("\n");
    //     printf("DIF_DWN: ");for (int j=0;j<19;j++) printf("%d ",dif_dwn[i][j]);printf("\n");
    //     printf("\n");
    // }
}
inline int GetAnswer(int x,int y){
    int ret=0;
    int now_min=1e8,now_max=0;
    if (x==y) return 0;
    for (int i=19;i>=0;i--) if (deep[f[x][i]]>=deep[y]){
        ret=max3(ret, max_num[x][i]-now_min, dif_upd[x][i]);
        now_min=min(now_min,min_num[x][i]);
        x=f[x][i];
    }
    for (int i=19;i>=0;i--) if (deep[f[y][i]]>=deep[x]){
        ret=max3(ret, now_max-min_num[y][i], dif_dwn[y][i]);
        now_max=max(now_max,max_num[y][i]);
        y=f[y][i];
    }
    if (x==y) return ret;
    for (int i=19;i>=0;i--) if (f[x][i]!=f[y][i]){
        ret=max3(ret,dif_upd[x][i],dif_dwn[y][i]);
        ret=max3(ret,max_num[x][i]-now_min,now_max-min_num[y][i]);
        now_min=min(now_min,min_num[x][i]);
        now_max=max(now_max,max_num[y][i]);
        x=f[x][i];y=f[y][i];
    }
    ret=max3(ret,dif_upd[x][0],dif_dwn[y][0]);
    ret=max3(ret,max_num[x][0]-now_min,now_max-min_num[y][0]);
    now_min=min(now_min,min_num[x][0]);
    now_max=max(now_max,max_num[y][0]);
    ret=max(ret,now_max-now_min);
    return ret;
}
int main(){
    while (scanf("%d",&n)!=-1){
        Init();
        for (int i=1;i<=n;i++) w[i]=read(),fa[i]=i;
        m=read();
        for (int i=1;i<=m;i++) e[i].x=read(),e[i].y=read(),e[i].w=read();
        Kruskal();printf("%d\n",sum);

        memset(fa,0,sizeof(fa));
        deep[1]=1;BuildTree(1);
        //printf("Deep:");for (int i=1;i<=n;i++) printf("%d ",deep[i]);printf("\n");
        //for (int i=1;i<=n;i++) printf("fa[%d]=%d\n",i,fa[i]);printf("\n");

        Build();
        q=read();
        for (int i=1;i<=q;i++){
            int x=read(),y=read();
            printf("%d\n",GetAnswer(x,y));
        }
    }
    return 0;
}

暂无评论


发表新的评论

所有评论都将经过博主审核。请勿填写无意义邮箱或发表无关评论、广告等,否则会被视为垃圾评论。

提交评论即表明你同意本网站使用 Cookie,并允许本站在后台记录你的邮箱、IP 地址等必要信息。
(提交一次评论后,本提示将不再展示)