ZOJ 3649 倍增法DP、树链剖分、tarjan并查集

Time Limit:5000MS     Memory Limit:65536KB     64bit IO Format:%lld & %llu
Submit  Status  Practice  ZOJ 3649

Description

There 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 nintegers 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


总得来说

最大生成树

维护四个域,从U到LCA的最大值,最小值,从U到LCA的最优值,从LCA到U的最优值


Tarjan离线处理

在处理并查集的时候维护四个域,必须要先维护上面的点,再维护下面的点

如果查询的终点是已经访问过的点,直接连接至LCA

然后全部遍历完了之后再维护并查集LCA

//      whn6325689
//		Mr.Phoebe
//		http://blog.csdn.net/u013007900
#include <algorithm>
#include <iostream>
#include <iomanip>
#include <cstring>
#include <climits>
#include <complex>
#include <fstream>
#include <cassert>
#include <cstdio>
#include <bitset>
#include <vector>
#include <deque>
#include <queue>
#include <stack>
#include <ctime>
#include <set>
#include <map>
#include <cmath>
#include <functional>
#include <numeric>
#pragma comment(linker, "/STACK:1024000000,1024000000")


using namespace std;

typedef long long ll;
typedef long double ld;
typedef pair<ll, ll> pll;
typedef complex<ld> point;
typedef pair<int, int> pii;
typedef pair<pii, int> piii;
typedef vector<int> vi;

#define CLR(x,y) memset(x,y,sizeof(x))
#define mp(x,y) make_pair(x,y)
#define pb(x) push_back(x)
#define lowbit(x) (x&(-x))
#define MID(x,y) (x+((y-x)>>1))
#define speed std::ios::sync_with_stdio(false);
#define eps 1e-9
#define PI acos(-1.0)
#define INF 0x3f3f3f3f
#define LLINF 1LL<<62

template<class T>
inline bool read(T &n)
{
    T x = 0, tmp = 1;
    char c = getchar();
    while((c < '0' || c > '9') && c != '-' && c != EOF) c = getchar();
    if(c == EOF) return false;
    if(c == '-') c = getchar(), tmp = -1;
    while(c >= '0' && c <= '9') x *= 10, x += (c - '0'),c = getchar();
    n = x*tmp;
    return true;
}
template <class T>
inline void write(T n)
{
    if(n < 0)
    {
        putchar('-');
        n = -n;
    }
    int len = 0,data[20];
    while(n)
    {
        data[len++] = n%10;
        n /= 10;
    }
    if(!len) data[len++] = 0;
    while(len--) putchar(data[len]+48);
}
//-----------------------------------

const int MAXN=50010;

struct edge
{
	int u,v,w;
	bool operator < (const edge & b) const{return w>b.w;}
}ee[MAXN<<1];

struct Edge
{
	int to,next;
}e[MAXN<<1],lca[MAXN<<1];

struct query
{
	int u,v,next;
	int id;
}q[MAXN<<1];

int h[MAXN],qh[MAXN],lh[MAXN];
int vis[MAXN],ma[MAXN],mi[MAXN],up[MAXN],down[MAXN];
int n,m,qq,tot,cnt,num,ans[MAXN],angry[MAXN];
void update(int u,int v);

struct disjoint
{
    int fa[MAXN];
    void init(int n)
    {
        for(int i=0;i<=n;i++)
            fa[i]=i;
    }
    int find_fa(int u)
    {
        return (u == fa[u]) ? fa[u] : (fa[u] = find_fa(fa[u]));
    }
    int find(int u)
	{
		if(u==fa[u])	return fa[u];
		int v=fa[u];
		fa[u]=find(fa[u]);
		update(u,v);
		return fa[u];
	}
    void merge(int u,int v)
    {
        fa[u]=v;
    }
}st;

void init(int n)
{
	CLR(h,-1);CLR(qh,-1);CLR(lh,-1);CLR(vis,0);
	tot=cnt=num=0;
	st.init(n);
}

void update(int u,int v)	//更新四个域
{
    //cout<<"fa:"<<u<<" "<<v<<endl;
	up[u]=max(ma[v]-mi[u],max(up[u],up[v]));
    down[u]=max(ma[u]-mi[v],max(down[u],down[v]));
    ma[u]=max(ma[u],ma[v]);
    mi[u]=min(mi[u],mi[v]);
}

void addedge(int u,int v)
{
	e[tot].to=v;
	e[tot].next=h[u];
	h[u]=tot++;
}

void addquery(int u,int v,int w)
{
	q[cnt].u=u;
    q[cnt].v=v;
    q[cnt].id=w;
    q[cnt].next=qh[u];
    qh[u]=cnt++;
}

void Kruskal()
{
	sort(ee,ee+m);
	int sum=0;
	for(int i=0;i<m;i++)
	{
		int u=st.find_fa(ee[i].u);
		int v=st.find_fa(ee[i].v);
		if(u!=v)
		{
			st.merge(v,u);
			sum+=ee[i].w;
			addedge(ee[i].u,ee[i].v);addedge(ee[i].v,ee[i].u);
			//cout<<ee[i].u<<" "<<ee[i].v<<endl;
		}
	}
	write(sum),putchar('\n');
}

void dfs(int u)
{
	vis[u]=1;
	for(int i=qh[u];~i;i=q[i].next)	//已经计算过的子树中的点的LCA
	{
		if(vis[q[i].v])
		{
			int xx=st.find(q[i].v);
			lca[num].to=i;	//如果是偶数则是顺,奇数逆
			lca[num].next=lh[xx];
			lh[xx]=num++;
		}
	}
	for(int i=h[u];~i;i=e[i].next)
	{
		if(vis[e[i].to])	continue;
		dfs(e[i].to);
		st.merge(e[i].to,u);	//位置高的为father
	}
	for(int i=lh[u];~i;i=lca[i].next)
	{
		int j=lca[i].to;
		int u=q[j].u,v=q[j].v;
		st.find(u);//st.find(v);
		if(j&1)	//如果是偶数则是顺,奇数逆
			swap(u,v);
		ans[q[j].id]=max(ma[v]-mi[u],max(up[u],down[v]));// 从u->v
		//cout<<q[j].id<<" "<<ans[q[j].id]<<endl;
	}
}

int main()
{
    //freopen("data.txt","r",stdin);
    //freopen("w.txt","w",stdout);
	while(read(n))
	{
		init(n);
		for(int i=1;i<=n;i++)
	    {
	        read(angry[i]);
	        ma[i]=mi[i]=angry[i];
	        up[i]=down[i]=0;
	    }
	    read(m);
	    for(int i=0;i<m;i++)
	        read(ee[i].u),read(ee[i].v),read(ee[i].w);
        read(qq);
     	for(int i=0,u,v;i<qq;i++)
        {
            read(u),read(v);
            addquery(u,v,i);addquery(v,u,i);
        }
   		Kruskal();
	    st.init(n);
	    dfs(1);
	    for(int i=0;i<qq;i++)
	    	write(ans[i]),putchar('\n');
	}
	return 0;
}


用倍增求LCA的方法在线求


#include <bits/stdc++.h>

struct node {
	int from, to, cost;
	bool operator < (const node &rhs) const {
		return cost > rhs.cost;
	}
};

node edge[50010];
int fa[50010];

int find(int x) {
	return fa[x] == x ? x : fa[x] = find(fa[x]);
}

int to[50010<<1], pre[50010<<1], tail[50010];
int e_tot = 0;

inline void add(int _from, int _to) {
	to[e_tot] = _to;
	pre[e_tot] = tail[_from];
	tail[_from] = e_tot++;
}

void getEdge(int n) {
	for(int i = 1; i <= n; ++i)
		fa[i] = i;
	int m;
	scanf("%d", &m);
	for(int i = 0; i < m; ++i)
		scanf("%d%d%d", &edge[i].from, &edge[i].to, &edge[i].cost);
	std::sort(edge, edge + m);
	long long ans = 0;
	e_tot = 0;
	memset(tail, -1, sizeof tail);
	for(int i = 0; i < m; ++i) {
		int pu = find(edge[i].from), pv = find(edge[i].to);
		if(pu != pv) {
			add(edge[i].from, edge[i].to);
			add(edge[i].to, edge[i].from);
			ans += edge[i].cost;
			fa[pu] = pv;
		}
	}
	printf("%lld\n", ans);
}

int min[50010][17], max[50010][17], ansFromU[50010][17], ansToU[50010][17], p[50010][17], dep[50010];
int value[50010];

void dfs(int now, int father, int depth) {
	memset(min[now], 0x3f, sizeof min[now]);
	memset(max[now], 0, sizeof max[now]);
	p[now][0] = father;
	min[now][0] = std::min(value[now], value[p[now][0]]);
	max[now][0] = std::max(value[now], value[p[now][0]]);
	ansFromU[now][0] = value[now] > value[p[now][0]] ? value[now] - value[p[now][0]] : 0;
	ansToU[now][0] = value[now] < value[p[now][0]] ? value[p[now][0]] - value[now] : 0;
	dep[now] = depth;
	for(int i = 1; i < 17; ++i) {
		p[now][i] = p[p[now][i-1]][i-1];
		min[now][i] = std::min(min[now][i-1], min[p[now][i-1]][i-1]);
		max[now][i] = std::max(max[now][i-1], max[p[now][i-1]][i-1]);
		ansFromU[now][i] = std::max(ansFromU[now][i-1], ansFromU[p[now][i-1]][i-1]);
		ansFromU[now][i] = std::max(ansFromU[now][i], max[now][i-1] - min[p[now][i-1]][i-1]);
		ansToU[now][i] = std::max(ansToU[now][i-1], ansToU[p[now][i-1]][i-1]);
		ansToU[now][i] = std::max(ansToU[now][i], max[p[now][i-1]][i-1] - min[now][i-1]);
	}
	for(int i = tail[now]; i != -1; i = pre[i]) {
		if(to[i] == p[now][0])
			continue;
		dfs(to[i], now, depth + 1);
	}
}

int getLca(int u, int v) {
	if(dep[u] > dep[v])
		std::swap(u, v);
	for(int delta = dep[v] - dep[u], i = 0; delta; delta >>= 1, ++i) {
		if(delta & 1)
			v = p[v][i];
	}
	if(u == v)
		return u;
	for(int i = 17 - 1; i >= 0; --i) {
		if(p[u][i] == p[v][i])
			continue;
		u = p[u][i], v = p[v][i];
	}
	return p[u][0];
}

int getFromU(int u, int lca) {
	int maxPre = 0, ans = 0;
	for(int delta = dep[u] - dep[lca], i = 0; delta; delta >>= 1, ++i) {
		if(delta & 1) {
			ans = std::max(ans, ansFromU[u][i]);
			ans = std::max(ans, maxPre - min[u][i]);
			maxPre = std::max(maxPre, max[u][i]);
			u = p[u][i];
		}
	}
	return ans;
}

int getToU(int u, int lca) {
	int minPre = 0x3f3f3f3f, ans = 0;
	for(int delta = dep[u] - dep[lca], i = 0; delta; delta >>= 1, ++i) {
		if(delta & 1) {
			ans = std::max(ans, ansToU[u][i]);
			ans = std::max(ans, max[u][i] - minPre);
			minPre = std::min(minPre, min[u][i]);
			u = p[u][i];
		}
	}
	return ans;
}

int getMax(int u, int lca) {
	int ret = 0;
	for(int delta = dep[u] - dep[lca], i = 0; delta; delta >>= 1, ++i) {
		if(delta & 1) {
			ret = std::max(ret, max[u][i]);
			u = p[u][i];
		}
	}
	return ret;
}

int getMin(int u, int lca) {
	int ret = 0x3f3f3f3f;
	for(int delta = dep[u] - dep[lca], i = 0; delta; delta >>= 1, ++i) {
		if(delta & 1) {
			ret = std::min(ret, min[u][i]);
			u = p[u][i];
		}
	}
	return ret;
}

void MAIN(int n) {
	for(int i = 1; i <= n; ++i)
		scanf("%d", &value[i]);
	getEdge(n);
	dfs(1, 1, 0);
	int q;
	scanf("%d", &q);
	for(int i = 0, u, v; i < q; ++i) {
		scanf("%d%d", &u, &v);
		int ans = 0, lca = getLca(u, v);
		ans = std::max(ans, getToU(u, lca));
		ans = std::max(ans, getFromU(v, lca));
		ans = std::max(ans, getMax(v, lca) - getMin(u, lca));
		printf("%d\n", ans);
	}
}

int main() {
	int n;
	while(scanf("%d", &n) > 0)
		MAIN(n);
	return 0;
}


树链剖分+线段树


#include <iostream>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <string>
#include <cstdio>
#include <vector>
#include <climits>
using namespace std;
#define lson(x) (x<<1)
#define rson(x) (x<<1|1)
#define pb push_back
const int MAXN=33333;
struct Node{
    int l,r,Min,Max,Pro[2];
}node[MAXN*6];

int f[MAXN];
int n,m;

int head[MAXN],tot;
int top[MAXN];
int fa[MAXN];
int deep[MAXN];
int num[MAXN];
int p[MAXN];
int fp[MAXN];
int son[MAXN];
int pos;

struct Seg{
    void pushup(int x){
        node[x].Max=max(node[lson(x)].Max, node[rson(x)].Max );
        node[x].Min=min(node[lson(x)].Min, node[rson(x)].Min );
        node[x].Pro[1]=max(node[lson(x)].Pro[1], node[rson(x)].Pro[1] );
        node[x].Pro[1]=max(node[x].Pro[1] , node[rson(x)].Max-node[lson(x)].Min );///maybe negative!!!!

        node[x].Pro[0]=max(node[lson(x)].Pro[0], node[rson(x)].Pro[0] );
        node[x].Pro[0]=max(node[x].Pro[0] , node[lson(x)].Max-node[rson(x)].Min );///maybe negative!!!!

    }
    void build(int l,int r,int x=1){
        node[x].l=l ; node[x].r=r;
        if(l==r){
            node[x].Max=f[fp[l]];///mind f
            node[x].Min=f[fp[l]];///mind f
            //cout<<l<<":"<<f[fp[l]]<<endl;
            node[x].Pro[0]=0;
            node[x].Pro[1]=0;
            return ;
        }
        int mid=(l+r)/2;
        build(l,mid,lson(x));
        build(mid+1,r,rson(x));
        pushup(x);

    }

    int query(int l,int r,int flag,int x=1 ){///mind !
        if(l==r) return 0;
        if(node[x].l >= l && node[x].r <= r){
            return node[x].Pro[flag];
        }
        int mid=(node[x].l + node[x].r)/2;
        //pushdown
        int ans=-INT_MAX;  ///is right?
        if(l<=mid){
                ans=max(ans, query(l,r ,flag,lson(x)) );
        }
        if(r>mid) {
                ans=max(ans,query(l,r, flag,rson(x) ) );
        }
        if(l<=mid && r>mid){
                if(flag==1){
                        int lMin=queryMin(l,r,lson(x)) , rMax= queryMax(l,r,rson(x));
                        ans=max(ans, rMax-lMin);
                }else{
                        int lMax=queryMax(l,r,lson(x)) , rMin= queryMin(l,r,rson(x));
                        ans=max(ans, lMax-rMin);
                }
        }
        pushup(x);
        return ans;
    }

    int queryMax(int l,int r,int x=1){///mind !
        if(node[x].l >= l && node[x].r <= r){
            return node[x].Max;
        }
        int mid=(node[x].l + node[x].r)/2;
        //pushdown
        int ans=-INT_MAX;  ///is right?
        if(l<=mid) ans=max(ans, queryMax(l,r,lson(x) ) );
        if(r>mid) ans=max(ans,queryMax(l,r,rson(x) ) );
        pushup(x);
        return ans;
    }

    int queryMin(int l,int r,int x=1){///mind !
        if(node[x].l >= l && node[x].r <= r){
            return node[x].Min;
        }
        int mid=(node[x].l + node[x].r)/2;
        //pushdown
        int ans=INT_MAX;  ///is right?
        if(l<=mid) ans=min(ans, queryMin(l,r,lson(x) ) );
        if(r>mid) ans=min(ans,queryMin(l,r,rson(x) ) );
        pushup(x);
        return ans;
    }

}seg;


struct Edge{
    int from,to,next , w;
}edge[MAXN*5] ;


void init(){
    tot=0;
    memset(head,-1,sizeof(head));
    pos=1;
    memset(son,-1,sizeof(son));
}
void addedge(int u,int v,int w){
    edge[tot].from = u;
    edge[tot].to = v;
    edge[tot].w = w;
    edge[tot].next = head[u];
    head[u] = tot++;
}
void dfs1(int u,int pre,int d){
    deep[u]=d;
    fa[u]=pre;
    num[u]=1;
    for(int i=head[u];i != -1; i=edge[i].next){
        int v=edge[i].to;
        if(v!=pre){
            dfs1(v,u,d+1);
            num[u]+=num[v];
            if(son[u] == -1 || num[v] > num[ son[u] ] )
                son[u]=v;
        }
    }
}

void getpos(int u,int sp){
    top[u]=sp;
    p[u]=pos++;
    fp[p[u]] = u;
    if(son[u]==-1) return;
    getpos(son[u] , sp);
    for(int i=head[u] ; i!=-1 ; i=edge[i].next){
        int v=edge[i].to;
        if(v!=son[u] && v!=fa[u])
            getpos(v,v);
    }

}

int GetQ(int u,int v){
    int f1=top[u] , f2=top[v];
    int tmp=0;
    vector<int > ANS ;
    vector<int >MAX[2] ,MIN[2];
    int flag=0;
    while(f1!=f2){
        if(deep[f1] < deep[f2]){
            swap(f1,f2);
            swap(u,v);
            flag^=1;
        }
        int ans=seg.query(p[f1],p[u] , flag ) ;

        ANS.push_back(ans);
        MAX[flag].push_back(seg.queryMax(p[f1],p[u])  );
        MIN[flag].push_back(seg.queryMin(p[f1],p[u])  );

        u=fa[f1];
        f1=top[u];
    }
    if(deep[u] > deep[v] ) swap(u,v)  ;
    else flag^=1;
    int ans=seg.query(p[u], p[v] ,flag) ;
    //cout<<p[u]<<" "<<p[v]<<" "<<flag<<endl;
    ANS.push_back(ans);
    if(ANS.size()==1) return ANS[0];
    MAX[flag].push_back(seg.queryMax(p[u], p[v])  );
    MIN[flag].push_back(seg.queryMin(p[u], p[v])  );
    int lastres=ANS[0];
    for(int i=MAX[1].size()-1 ; i>=0 ; i--){
        MAX[0].pb(MAX[1][i]);
        MIN[0].pb(MIN[1][i]);

    }
    int minn=MIN[0][0];
    for(int i=1; i<ANS.size() ; i++){
            lastres=max(lastres,ANS[i]);
            lastres=max(lastres,MAX[0][i]-minn);
            minn=min(minn,MIN[0][i]);
    }
    return lastres;

}

struct Bing{
    int par[MAXN];
    void init(int n){
        for(int i=1;i<=n;i++) par[i]=i;
    }
    int find(int u){
        if(par[u]==u) return u;
        else return par[u]=find(par[u]);
    }
    void unite(int u,int v){
        u=find(u);
        v=find(v);
        if(u==v) return;
        par[u]=v;
    }
}bing;

bool cmp(const Edge& e1,const Edge& e2){
    return e1.w < e2.w;
}
int kruskal(int edgeNum , vector<pair<int ,int > >& newG ){
    sort(edge,edge+edgeNum,cmp);
    bing.init(n);
    int res=0;
    for(int i=0;i<edgeNum;i++){
        Edge e=edge[i];
        if(bing.find(e.from ) != bing.find(e.to ) ){
            bing.unite(e.from , e.to);
            newG.push_back(make_pair(e.from, e.to) );
            res+=e.w;

        }
    }
    return res;
}

int main()///mind lld??
{
    //freopen("in.txt","r",stdin);
    while(scanf("%d",&n)==1)
    {
        init();

        for(int i=1;i<=n;i++) scanf("%d",&f[i]);
        scanf("%d",&m);
        for(int i=1;i<=m;i++){
            int u,v,w;
            scanf("%d%d%d",&u,&v,&w);
            w=-w;
            addedge(u,v,w);
            addedge(v,u,w);
        }
        vector<pair<int ,int > > newG;
        int sum=kruskal(tot,newG);
        sum=-sum;
        printf("%d\n",sum);

        tot=0;
        memset(head,-1,sizeof(head));
        for(int i=0;i<newG.size(); i++){
            int u=newG[i].first ,v=newG[i].second;
            addedge(u,v,0);
            addedge(v,u,0);
        }

        dfs1(1,0,0);
        getpos(1,1);
        seg.build(1,pos-1);

        //cout<<seg.query(5,6,0)<<endl;

        int que;
        scanf("%d",&que);

        while(que--){
            int u,v;
            scanf("%d%d",&u,&v);
            int ans=GetQ(u,v);
            printf("%d\n",ans);
        }
    }
    return 0;
}



你可能感兴趣的:(ZOJ 3649 倍增法DP、树链剖分、tarjan并查集)