[zoj3649]Social Net

here are n individuals(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 ai(1 <= ai <= 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 bi(1 <= bi <= 100000). We have q operations(1 <= q <= 30000): Person X wants to contact person Y, 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, … ,ci. Here ci means the angry value of the ith people in the sequence.

We attempt to find the maximum ck-cj (ck >= cj, j <= k).

Example:

The sequence is 3(X), 4, 5, 6, 7, 5, 9, 4, 11(Y). The maximum ck-cj is 11-3=8.

The sequence is 3(X), 4, 5, 6, 7, 5, 9, 2, 11(Y). The maximum ck-cj is 11-2=9.

The sequence is 3(X), 10, 2, 5(Y). The maximum ck-cj is 10-3=7.

Input

The input contains multiple test cases. Each test case begins with a line containing a single integer n. The following line contains n integers bi.

The subsequent line describe the number of relations m(n <= m <= 50000). The next m lines contain the information about relations: x, y, ai. Their friendship value is ai.

Afterward gives q. The next q lines contain the operations: x, y. person X wants to contact person Y.

Output

For each case, print maximum sum of friendship value of the net on the first line.

The next q 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

提交地址


题意:有n个人,每个人有bi的愤怒值,并且给你m个关系,每个关系有ai的值。
第一问,要求你建出一个n个人都在的关系网,并且要求这个关系网的值最大。
第二问,有q个询问,询问在这个网中x到y的愤怒值组成的序列,满足j>i,bj-bi的最大值是多少(i,j,是指在x到y的序列上的序号)

题解:
第一问,显然用kruscal就能够解决。
第二问,求树上的x到y,由于q和n的数据规模,单纯的dfs肯定会超时,这时候我们就考虑用log级的算法。而快速的处理x到y,当然是用倍增了。
而这题需要在序号限制下求的最大差,这时候,我们就考虑在倍增的框架下dp。
维护四个数组:
mx[u][i]:u到2的i次方祖先的最大值
mi[u][i]:u到2的i次方祖先的最小值
dp[u][i]:u到2的i次方祖先线段上的最大差(祖先减去子孙)
dp2[u][i]:u到2的i次方祖先线段上的最大差(子孙减去祖先)
最后的答案,就考虑x到lca的最大差和y到lca的最大差,以及跨段的最大差就可以得到了。

/*
ID: Rec
PROG: test
LANG: C++
*/
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<queue>
#include<algorithm>
#include<stack>
#define N 60000
std::queue<int>q; 
struct aa{
    int p,next;
}da[N*3];
struct noz{
    int x,y,len;
}line[N*2];
int fa[N],dd,tou[N],mx[N][21],mi[N][21],fm[N][21],dp[N][21],dp2[N][21];
int n,b[N],m,dep[N];
bool vis[N];
int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
void add(int x,int y){
    da[++dd].p=y;da[dd].next=tou[x];tou[x]=dd;
    da[++dd].p=x;da[dd].next=tou[y];tou[y]=dd;
}
void bfs(){
    q.push(1);
    vis[1]=1;
    dep[1]=1;
    while (!q.empty()){
        int u=q.front();q.pop();
        for (int i=tou[u];i;i=da[i].next){
            int v=da[i].p;
            if (!vis[v]){
                dep[v]=dep[u]+1;
                q.push(v),vis[v]=1;
                fm[v][0]=u;
                mx[v][0]=std::max(b[v],b[u]);
                mi[v][0]=std::min(b[v],b[u]);
                dp[v][0]=b[u]-b[v];
                dp2[v][0]=b[v]-b[u];
            }
        }
        for (int i=1;i<=20;i++){
            fm[u][i]=fm[fm[u][i-1]][i-1];
            if (!fm[u][i])break;
            mx[u][i]=std::max(mx[u][i-1],mx[fm[u][i-1]][i-1]);
            mi[u][i]=std::min(mi[u][i-1],mi[fm[u][i-1]][i-1]);
            dp[u][i]=std::max(dp[u][i-1],mx[fm[u][i-1]][i-1]-mi[u][i-1]);
            dp[u][i]=std::max(dp[u][i],dp[fm[u][i-1]][i-1]);
            dp2[u][i]=std::max(dp2[u][i-1],mx[u][i-1]-mi[fm[u][i-1]][i-1]);
            dp2[u][i]=std::max(dp2[u][i],dp2[fm[u][i-1]][i-1]);
        }
    }
}
int lca(int x,int y){
    if (dep[x]<dep[y])std::swap(x,y);
    for (int i=20;i>=0;i--)
      if (dep[x]-dep[y]>=1<<i)x=fm[x][i];
    if (x==y)return x;
    for (int i=20;i>=0;i--)
      if (fm[x][i]!=fm[y][i]){
        x=fm[x][i];
        y=fm[y][i];
      }
    if (x==y)return x;
    else return fm[x][0];
}
int getMAX(int x,int goal){
    int ma=0;
    for (int i=20;i>=0;i--)
      if (dep[x]-dep[goal]>=1<<i){
        ma=std::max(ma,mx[x][i]);
        x=fm[x][i];
      }
    return ma;
} 
int  getMIN(int x,int goal){
    int ma=0x5f5f5f5f;
    for (int i=20;i>=0;i--)
      if (dep[x]-dep[goal]>=1<<i){
        ma=std::min(ma,mi[x][i]);
        x=fm[x][i];
      }
    return ma;
}
int opre1(int x,int goal){
    int ma=0x5f5f5f5f;
    int num=0;
    for (int i=20;i>=0;i--)
     if (dep[x]-dep[goal]>=1<<i){
        num=std::max(num,dp[x][i]);
        num=std::max(num,mx[x][i]-ma);
        ma=std::min(ma,mi[x][i]);
        x=fm[x][i];
    }
    return num;
}
int opre2(int x,int goal){
    int ma=-0x5f5f5f5f;
    int num=0;
    for (int i=20;i>=0;i--)
     if (dep[x]-dep[goal]>=1<<i){
        num=std::max(num,dp2[x][i]);
        num=std::max(num,ma-mi[x][i]);
        ma=std::max(ma,mx[x][i]);
        x=fm[x][i];
    }
    return num;
}
bool cmp(noz a,noz b){return a.len>b.len;}
int main(){
    freopen("test.in","r",stdin);
    freopen("test.out","w",stdout);
    while (scanf("%d",&n)!=EOF){
        dd=0;
        memset(tou,0,sizeof(tou));
        memset(vis,0,sizeof(vis));
        memset(dep,0,sizeof(dep));
        for (int i=1;i<=n;i++)fa[i]=i;
        for (int i=1;i<=n;i++)
          for (int j=0;j<=20;j++)
            mx[i][j]=dp[i][j]=dp2[i][j]=fm[i][j]=0,mi[i][j]=0x5f5f5f5f;
        for (int i=1;i<=n;i++)scanf("%d",&b[i]); 
        scanf("%d",&m);
        for (int i=1;i<=m;i++)scanf("%d%d%d",&line[i].x,&line[i].y,&line[i].len);
        std::sort(line+1,line+1+m,cmp);
        int tot=0;int sum=0;
        for (int i=1;i<=m;i++){
            int fx=find(line[i].x);
            int fy=find(line[i].y);
            if (fx!=fy){
                fa[fx]=fy;
                ++tot;
                sum+=line[i].len;
                add(line[i].x,line[i].y);
                if (tot==n-1)break;
            }
        }
        printf("%d\n",sum);
        bfs();
        int q;
        scanf("%d",&q);
        for (int i=1;i<=q;i++){
            int x,y;
            scanf("%d%d",&x,&y);
            int LCA=lca(x,y);
            int x1=opre1(x,LCA);
            int x2=opre2(y,LCA);
            printf("%d\n",std::max(std::max(x1,x2),getMAX(y,LCA)-getMIN(x,LCA)));
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值