美文网首页
图论(二)

图论(二)

作者: 云中翻月 | 来源:发表于2019-09-28 20:19 被阅读0次

    强连通分量

    POJ 3180: The Cow Prom
    找出有多少个点数大于等于2的scc即可。
    代码如下

    /*
    
    */
    #define method_1
    #ifdef method_1
    /*
    找出有多少个点数大于等于2的scc即可。
    */
    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cmath>
    #include<set>
    #include<map>
    #include<queue>
    #include<stack>
    #include<vector>
    #include<cstring>
    #include<cstdlib>
    #include<iomanip>
    #define D(x) cout<<#x<<" = "<<x<<"  "
    #define E cout<<endl
    using namespace std;
    typedef long long ll;
    typedef pair<int,int>pii;
    const int maxn=10000+5;
    const int maxm=50000+5;
    const int INF=0x3f3f3f3f;
    int n,m,tot=1,cnt=0,ans=0,head[maxn],dfn[maxn],low[maxn],st[maxn],inx[maxn],c[maxn],num=0,top=0,chu[maxn],ru[maxn];
    vector<int>scc[maxn];
    struct node{
        int from,to;
    }edge[maxm<<1];
    void add(int from,int to){
        edge[++tot].to=to;
        edge[tot].from=head[from];
        head[from]=tot;
    }
    void tarjan(int x){
        dfn[x]=low[x]=++num;
        st[++top]=x;
        inx[x]=1;
        for(int i=head[x];i;i=edge[i].from){
            int y=edge[i].to;
            if(!dfn[y]){
                tarjan(y);
                low[x]=min(low[x],low[y]);
            }
            else if(inx[y]){
                low[x]=min(low[x],dfn[y]);
            }
        }
        if(low[x]==dfn[x]){
            int y;
            cnt++;
            do{
                y=st[top];
                top--;
                inx[y]=0;
                c[y]=cnt;
                scc[cnt].push_back(y); 
            }while(x!=y);
        }
    }
    int main() {
    //  ios::sync_with_stdio(false);
        //freopen("The Cow Prom.in","r",stdin);
        cin>>n>>m; //n>=2
        int from,to;
        for(int i=1;i<=m;i++){
            scanf("%d%d",&from,&to);
            add(from,to);
        }
        for(int i=1;i<=n;i++){
            if(!dfn[i]) tarjan(i);
        }
        for(int i=1;i<=cnt;i++) if(scc[i].size()>=2) ans++;
        cout<<ans;
        return 0;
    }
    #endif
    #ifdef method_2
    /*
    
    */
    
    #endif
    #ifdef method_3
    /*
    
    */
    
    #endif
    

    POJ 1236: Network of Schools
    缩点后先特判只有一个scc的情况。
    考虑每个scc的出入度,设出度为0的scc有p个,入度为0的scc有q个。
    第一问的答案显然是p,第二问的答案是max(p,q)
    解释下第二问答案的来历。
    显然,每加入一条有向边,最少让一个点的出度/入度非0,最多让两个点的出度/入度非0。
    于是,我们先尽量采取第二种决策。即先将min(p,q)对点解决。然后剩下来的max(p,q)-min(p,q)个点采用第一种决策。
    代码如下

    /*
    
    */
    #define method_1
    #ifdef method_1
    /*
    缩点后先特判只有一个scc的情况 
    考虑每个scc的出入度,设出度为0的scc有p个,入度为0的scc有q个。
    第一问的答案显然是p,第二问的答案是max(p,q)
    解释下第二问答案的来历。
    显然,每加入一条有向边,最少让一个点的出度/入度非0,最多让两个点的出度/入度非0。
    于是,我们先尽量采取第二种决策。即先将min(p,q)对点解决。然后剩下来的max(p,q)-min(p,q)个点采用第一种决策。 
    */
    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cmath>
    #include<set>
    #include<map>
    #include<queue>
    #include<stack>
    #include<vector>
    #include<cstring>
    #include<cstdlib>
    #include<iomanip>
    #define D(x) cout<<#x<<" = "<<x<<"  "
    #define E cout<<endl
    using namespace std;
    typedef long long ll;
    typedef pair<int,int>pii;
    const int maxn=100+5;
    const int INF=0x3f3f3f3f;
    int n,tot=1,cnt=0,head[maxn],p=0,q=0,dfn[maxn],low[maxn],st[maxn],inx[maxn],c[maxn],num=0,top=0,chu[maxn],ru[maxn];
    vector<int>scc[maxn];
    struct node{
        int from,to;
    }edge[maxn*maxn*2];
    void add(int from,int to){
        edge[++tot].to=to;
        edge[tot].from=head[from];
        head[from]=tot;
    }
    void tarjan(int x){
        dfn[x]=low[x]=++num;
        st[++top]=x;
        inx[x]=1;
        for(int i=head[x];i;i=edge[i].from){
            int y=edge[i].to;
            if(!dfn[y]){
                tarjan(y);
                low[x]=min(low[x],low[y]);
            }
            else if(inx[y]){
                low[x]=min(low[x],dfn[y]);
            }
        }
        if(low[x]==dfn[x]){
            int y;
            cnt++;
            do{
                y=st[top];
                top--;
                inx[y]=0;
                c[y]=cnt;
                scc[cnt].push_back(y); 
            }while(x!=y);
        }
    }
    int main() {
        ios::sync_with_stdio(false);
        //freopen("Network of Schools.in","r",stdin);
        cin>>n;
        int v;
        for(int i=1;i<=n;i++){
            while(cin>>v&&v){
                add(i,v);           
            }
        }
        for(int i=1;i<=n;i++){
            if(!dfn[i]) tarjan(i);
        }
        if(cnt==1){
            cout<<"1"<<endl<<"0";
            return 0; 
        }
        for(int i=1;i<=n;i++){
            for(int j=head[i];j;j=edge[j].from){
                int y=edge[j].to;
                if(c[i]!=c[y]){
                    chu[c[i]]++;
                    ru[c[y]]++;
                }
            }
        }
        for(int i=1;i<=cnt;i++){
            if(chu[i]==0) q++;
            if(ru[i]==0) p++;
        }
        cout<<p<<endl<<max(p,q);
        return 0;
    }
    #endif
    #ifdef method_2
    /*
    
    */
    
    #endif
    #ifdef method_3
    /*
    
    */
    
    #endif
    

    2-SAT

    POJ 3678: Katu Puzzle
    2-SAT模板题。
    注意建边时,如果给出的某个条件已经能够确定其取值,那么就用一种能够导出矛盾的建边方法。
    例如,如果from&to=1,意味着from和to必定都只能取1。因此建边时建立

    add(from,from+n);
    add(to,to+n);
    

    这样一来,当一个为0的时候,就能导出矛盾。
    如果给出的某个条件已经不能够确定其取值,那么就用一种能够“假设型”的建边方法。
    例如,如果from&to=0,意味着如果from为1,那么to就要为0。这是from的值不能确定,所以就按照如下方式建边。

    add(from+n,to);
    add(to+n,from);
    

    即from为1时,能够推出to为0,to为1时,同样能推出from为0。
    代码如下

    /*
    
    */
    #define method_1
    #ifdef method_1
    /*
    2-SAT模板题。
    注意建边时,如果给出的某个条件已经能够确定其取值,那么就用一种能够导出矛盾的建边方法。
    例如,如果from&to=1,意味着from和to必定都只能取1。因此建边时建立
    add(from,from+n);
    add(to,to+n);
    这样一来,当一个为0的时候,就能导出矛盾。
    如果给出的某个条件已经不能够确定其取值,那么就用一种能够“假设型”的建边方法。
    例如,如果from&to=0,意味着如果from为1,那么to就要为0。这是from的值不能确定,所以就按照如下方式建边。
    add(from+n,to);
    add(to+n,from);
    即from为1时,能够推出to为0,to为1时,同样能推出from为0。 
    */
    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cmath>
    #include<set>
    #include<map>
    #include<queue>
    #include<stack>
    #include<vector>
    #include<cstring>
    #include<cstdlib>
    #include<iomanip>
    #include<ctime>
    #include<string>
    #define D(x) cout<<#x<<" = "<<x<<"  "
    #define E cout<<endl
    using namespace std;
    typedef long long ll;
    typedef pair<int,int>pii;
    const int maxn=1000*2+5;
    const int maxm=1000000*4+5;
    const int INF=0x3f3f3f3f;
    int n,m;
    int head[maxn],tot=1;
    int dfn[maxn],low[maxn],cnt=0,st[maxn],num=0,inx[maxn],c[maxn],top=0;
    vector<int>scc[maxn];
    struct node{
        int from,to;
    }edge[maxm];
    void add(int from,int to){
        edge[++tot].from=head[from],head[from]=tot,edge[tot].to=to;
    }
    void tarjan(int x){
        dfn[x]=low[x]=++num;
        st[++top]=x;
        inx[x]=1;
        for(int i=head[x];i;i=edge[i].from){
            int y=edge[i].to;
            if(!dfn[y]){
                tarjan(y);
                low[x]=min(low[x],low[y]);
            }
            else if(inx[y]){
                low[x]=min(low[x],dfn[y]);
            }
        }
        if(dfn[x]==low[x]){
            int y;
            cnt++;
            do{
                y=st[top];
                top--;
                inx[y]=0;
                c[y]=cnt;
                scc[cnt].push_back(y);
            }while(x!=y);
        }
    }
    int main() {
    //  ios::sync_with_stdio(false);
        //freopen("Katu Puzzle.in","r",stdin);
        scanf("%d%d",&n,&m);
        int from,to,op;
        char s[10];
        for(int i=1;i<=m;i++){
            scanf("%d%d%d%s",&from,&to,&op,s);
            from++,to++;
            if(s[0]=='A'){ 
                if(op==0){
                    add(from+n,to);
                    add(to+n,from);
                }
                if(op==1){ //from&&to=1 表示两个都必须是1,因此如果一个是0,就导出矛盾
                //不能按照注释中建图,原因在于这个条件确定了from和to的取值,不能带入到“如果,就”的语境中去。 
    //              add(from+n,to+n);
    //              add(to+n,from+n);
                    add(from,from+n);
                    add(to,to+n);
                }
            } 
            if(s[0]=='O'){
                if(op==0){ //from||to=0 表示两个都必须是0,因此如果一个不是0,就导出矛盾 
    //              add(from,to);
    //              add(to,from);
                    add(from+n,from);
                    add(to+n,to); 
                }
                if(op==1){
                    add(from,to+n);
                    add(to,from+n);
                }
            }
            if(s[0]=='X'){
                if(op==0){
                    add(from,to);
                    add(to,from);
                    add(from+n,to+n);
                    add(to+n,from+n);
                }
                if(op==1){
                    add(from+n,to);
                    add(from,to+n);
                    add(to,from+n);
                    add(to+n,from);
                }
            }
        }
        for(int i=1;i<=2*n;i++) if(!dfn[i]) tarjan(i);
        int flag=1;
        for(int i=1;i<=n;i++) if(c[i]==c[i+n]) flag=0;
        flag?cout<<"YES":cout<<"NO";
        return 0;
    }
    #endif
    #ifdef method_2
    /*
    
    */
    
    #endif
    #ifdef method_3
    /*
    
    */
    
    #endif
    

    POJ 2723: Get Luffy Out
    读懂题意后,2-SAT正常建图即可。
    对于两个有关系的钥匙a和b,建边(a+2n,b),(b+2n,a)表示如果选了a就不能选b,选了b就不能选a。
    同理,对于门锁需要的钥匙a和b,显然满足a||b=0,即如果a为1,b就为0,a为0,b就为1,于是建边(a,b+2n),(b,a+2n)。
    因为有开门顺序,所以二分答案后,每次重新建一次图,然后用tarjan判断是否矛盾即可。
    注意一个天坑:题目说的是有n对钥匙,就有2n个钥匙,就有4n个布尔变量。也就是说,a的对应点是a+2*n而不是a+n。
    代码如下

    /*
    
    */
    #define method_1
    #ifdef method_1
    /*
    读懂题意后,2-SAT正常建图即可。
    对于两个有关系的钥匙a和b,建边(a+2*n,b),(b+2*n,a)表示如果选了a就不能选b,选了b就不能选a。
    同理,对于门锁需要的钥匙a和b,显然满足a||b=0,即如果a为1,b就为0,a为0,b就为1,于是建边(a,b+2*n),(b,a+2*n)。 
    因为有开门顺序,所以二分答案后,每次重新建一次图,然后用tarjan判断是否矛盾即可。
    注意一个天坑:题目说的是有n对钥匙,就有2*n个钥匙,就有4*n个布尔变量。也就是说,a的对应点是a+2*n而不是a+n。 
    */
    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cmath>
    #include<set>
    #include<map>
    #include<queue>
    #include<stack>
    #include<vector>
    #include<cstring>
    #include<cstdlib>
    #include<iomanip>
    #include<ctime>
    #include<string>
    #define D(x) cout<<#x<<" = "<<x<<"  "
    #define E cout<<endl
    using namespace std;
    typedef long long ll;
    typedef pair<int,int>pii;
    const int maxn=1024*4+5; //题目说的是有n对钥匙,就有2*n个钥匙,就有4*n个布尔变量。 
    const int maxm=1024*4+2048*4+5;
    const int INF=0x3f3f3f3f;
    int n,m;
    int head[maxn],tot=1;
    int dfn[maxn],low[maxn],cnt,st[maxn],num,inx[maxn],c[maxn],top;
    //vector<int>scc[maxn];
    struct node{
        int from,to;
    }edge[maxm];
    inline void init(){
        tot=1;memset(head,0,sizeof(head));
    //  for(int i=1;i<=cnt;i++) scc[i].clear();
        cnt=0,num=0,top=0;
        memset(dfn,0,sizeof(dfn));
        memset(low,0,sizeof(low));
        memset(st,0,sizeof(st));
        memset(inx,0,sizeof(inx));
        memset(c,0,sizeof(c));
    }
    inline void add(int from,int to){
        edge[++tot].from=head[from],head[from]=tot,edge[tot].to=to;
    }
    void tarjan(int x){
        dfn[x]=low[x]=++num;
        st[++top]=x;
        inx[x]=1;
        for(int i=head[x];i;i=edge[i].from){
            int y=edge[i].to;
            if(!dfn[y]){
                tarjan(y);
                low[x]=min(low[x],low[y]);
            }
            else if(inx[y]){
                low[x]=min(low[x],dfn[y]);
            }
        }
        if(dfn[x]==low[x]){
            int y;
            cnt++;
            do{
                y=st[top];
                top--;
                inx[y]=0;
                c[y]=cnt;
    //          scc[cnt].push_back(y);
            }while(x!=y);
        }
    }
    int a[maxm],b[maxm],e1[maxn],e2[maxn];
    bool check(int mid){
        init();
        for(int i=1;i<=n;i++) add(e1[i]+2*n,e2[i]),add(e2[i]+2*n,e1[i]);
        for(int i=1;i<=mid;i++){
            add(a[i],b[i]+2*n),add(b[i],a[i]+2*n);
        }
        for(int i=1;i<=4*n;i++) if(!dfn[i]) tarjan(i);
        for(int i=1;i<=2*n;i++) if(c[i]==c[i+2*n]) return 0;
        return 1;
    }
    int main() {
    //  ios::sync_with_stdio(false);
        //freopen("Get Luffy Out.in","r",stdin);
        while(~scanf("%d%d",&n,&m)){
            if(!n&&!m) break;
            for(int i=1;i<=n;i++){
    //          cin>>e1[i]>>e2[i];
                scanf("%d%d",&e1[i],&e2[i]);
                e1[i]++,e2[i]++;
            }
    //      for(int i=1;i<=m;i++) cin>>a[i]>>b[i],a[i]++,b[i]++;
            for(int i=1;i<=m;i++) scanf("%d%d",&a[i],&b[i]),a[i]++,b[i]++;
            int l=0,r=m,ans;
            while(l<=r){
                int mid=l+r>>1;
                if(check(mid)) l=mid+1,ans=mid;
                else r=mid-1;
            } 
    //      cout<<l<<endl;
            printf("%d\n",ans);
    //      int l=0,r=m;
    //      while(l<r){
    //          int mid=l+r+1>>1;
    //          if(check(mid)) l=mid;
    //          else r=mid-1;
    //      } 
    ////        cout<<l<<endl;
    //      printf("%d\n",l);
        }
        return 0;
    }
    #endif
    #ifdef method_2
    /*
    
    */
    
    #endif
    #ifdef method_3
    /*
    
    */
    
    #endif
    

    POJ 2749: Building roads
    二分答案后建图即可。
    注意两个点i和j之间的建图过程。
    显然,如果i和j只见满足某个距离条件时,很难确定他们的选择。
    但如果他们间的距离出现矛盾时,就比较容易建立“只要一个确定,另一个也确定”的条件。
    假设目前二分答案为mid,两个中转点之间的距离为len,i到第一个点的距离为d1[i],i到第二个点的距离为d2[i]。
    则按照如下方式建图。

    if(d1[i]+d1[j]>mid){ //i和j不能同时选择通过第一个中转点。即如果i和j中的一个选了第一个中转点,另一个就要选择第二个中转点 
        add(i,j+n); //如果i选择了第一个中转点,j就只能选择第二个中转点 
        add(j,i+n);
    }
    if(d2[i]+d2[j]>mid){ //与上同理 
        add(i+n,j);
        add(j+n,i);
    }
    if(d1[i]+d2[j]+len>mid){ //如果i选择了第一个中转点,j选择了第二个中转点,i和j之间的距离会不满足当前答案。即i和j要选择同一个中转点。 
        add(i,j);
        add(j+n,i+n);
    }
    if(d2[i]+d1[j]+len>mid){ //与上同理 
        add(i+n,j+n);
        add(j,i);
    }
    

    代码如下

    /*
    
    */
    #define method_1
    #ifdef method_1
    /*
    二分答案后建图即可。
    注意两个点i和j之间的建图过程。
    显然,如果i和j只见满足某个距离条件时,很难确定他们的选择。
    但如果他们间的距离出现矛盾时,就比较容易建立“只要一个确定,另一个也确定”的条件。 
    假设目前二分答案为mid,两个中转点之间的距离为len,i到第一个点的距离为d1[i],i到第二个点的距离为d2[i]。
    则按照如下方式建图。
    if(d1[i]+d1[j]>mid){ //i和j不能同时选择通过第一个中转点。即如果i和j中的一个选了第一个中转点,另一个就要选择第二个中转点 
        add(i,j+n); //如果i选择了第一个中转点,j就只能选择第二个中转点 
        add(j,i+n);
    }
    if(d2[i]+d2[j]>mid){ //与上同理 
        add(i+n,j);
        add(j+n,i);
    }
    if(d1[i]+d2[j]+len>mid){ //如果i选择了第一个中转点,j选择了第二个中转点,i和j之间的距离会不满足当前答案。即i和j要选择同一个中转点。 
        add(i,j);
        add(j+n,i+n);
    }
    if(d2[i]+d1[j]+len>mid){ //与上同理 
        add(i+n,j+n);
        add(j,i);
    }
    */
    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cmath>
    #include<set>
    #include<map>
    #include<queue>
    #include<stack>
    #include<vector>
    #include<cstring>
    #include<cstdlib>
    #include<iomanip>
    #include<ctime>
    #include<string>
    #define D(x) cout<<#x<<" = "<<x<<"  "
    #define E cout<<endl
    using namespace std;
    typedef long long ll;
    typedef pair<int,int>pii;
    const int maxn=2000+5;
    const int maxm=5000005+5;
    const int INF=0x3f3f3f3f;
    int n,A,B,x[maxn],y[maxn],h1[maxn],h2[maxn],f1[maxn],f2[maxn];
    int len;//两个中间点之间的距离
    int d1[maxn],d2[maxn];
    int head[maxn],tot;
    int dfn[maxn],low[maxn],cnt,st[maxn],num,inx[maxn],c[maxn],top;
    struct node {
        int from,to;
    } edge[maxm];
    void add(int from,int to) {
        edge[++tot].from=head[from],head[from]=tot,edge[tot].to=to;
    }
    void tarjan(int x) {
        dfn[x]=low[x]=++num;
        st[++top]=x;
        inx[x]=1;
        for(int i=head[x]; i; i=edge[i].from) {
            int y=edge[i].to;
            if(!dfn[y]) {
                tarjan(y);
                low[x]=min(low[x],low[y]);
            } else if(inx[y]) {
                low[x]=min(low[x],dfn[y]);
            }
        }
        if(dfn[x]==low[x]) {
            int y;
            cnt++;
            do {
                y=st[top];
                top--;
                inx[y]=0;
                c[y]=cnt;
            } while(x!=y);
        }
    }
    int dis(int i,int j) {
        return abs(x[i]-x[j])+abs(y[i]-y[j]);
    }
    inline void init() {
        tot=1;
        memset(head,0,sizeof(head));
        cnt=0,num=0,top=0;
        memset(dfn,0,sizeof(dfn));
        memset(low,0,sizeof(low));
        memset(st,0,sizeof(st));
        memset(inx,0,sizeof(inx));
        memset(c,0,sizeof(c));
    }
    bool check(int mid) {
        init();
        for(int i=1; i<=A; i++) add(h1[i],h2[i]+n),add(h2[i],h1[i]+n),add(h2[i]+n,h1[i]),add(h1[i]+n,h2[i]);
        for(int i=1; i<=B; i++) add(f1[i],f2[i]),add(f2[i],f1[i]),add(f2[i]+n,f1[i]+n),add(f1[i]+n,f2[i]+n);
        for(int i=1; i<=n; i++) {
            for(int j=i+1; j<=n; j++) {
    //          if(i==j) continue;
                if(d1[i]+d1[j]>mid) {
                    add(i,j+n);
                    add(j,i+n);
                }
                if(d2[i]+d2[j]>mid) {
                    add(i+n,j);
                    add(j+n,i);
                }
                if(d1[i]+d2[j]+len>mid) {
                    add(i,j);
                    add(j+n,i+n);
                }
                if(d2[i]+d1[j]+len>mid) {
                    add(i+n,j+n);
                    add(j,i);
                }
            }
        }
        for(int i=1; i<=2*n; i++) if(!dfn[i]) tarjan(i);
        for(int i=1; i<=n; i++) if(c[i]==c[i+n]) return false;
        return true;
    }
    int main() {
    //  ios::sync_with_stdio(false);
        //freopen("Building roads.in","r",stdin);
        scanf("%d%d%d",&n,&A,&B); //1~n n+1~2n
        scanf("%d%d%d%d",&x[2*n+1],&y[2*n+1],&x[2*n+2],&y[2*n+2]); //2n+1 2n+2
        len=dis(2*n+1,2*n+2);
    //  D(len);E;
        for(int i=1; i<=n; i++) {
            scanf("%d%d",&x[i],&y[i]);
            d1[i]=dis(i,2*n+1),d2[i]=dis(i,2*n+2);
    //      D(d1[i]);D(d2[i]);E;
        }
        for(int i=1; i<=A; i++) scanf("%d%d",&h1[i],&h2[i]);
        for(int i=1; i<=B; i++) scanf("%d%d",&f1[i],&f2[i]);
        int l=0,r=INF;
        while(l<r) {
            int mid=l+r>>1;
            if(check(mid)) r=mid;
            else l=mid+1;
        }
        if(l==INF) printf("-1");
        else printf("%d",l);
        return 0;
    }
    #endif
    #ifdef method_2
    /*
    
    */
    
    #endif
    #ifdef method_3
    /*
    
    */
    
    #endif
    

    LCA

    POJ 1986: Distance Queries
    模板题,不赘述。
    代码如下

    /*
    
    */
    #define method_1
    #ifdef method_1
    /*
    
    */
    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cmath>
    #include<set>
    #include<map>
    #include<queue>
    #include<stack>
    #include<vector>
    #include<cstring>
    #include<cstdlib>
    #define D(x) cout<<#x<<" = "<<x<<"  "
    #define E cout<<endl
    using namespace std;
    typedef long long ll;
    typedef pair<int,int>pii;
    const int maxn=3e6+5;
    const int INF=0x3f3f3f3f;
    int n,m;
    struct node {
        int from,to,w;
    } edge[maxn];
    int t,head[maxn],tot=0,ans[maxn],fa[maxn][20],d[maxn],dis[maxn];
    void add(int u,int v,int w) {
        edge[++tot].to=v;
        edge[tot].from=head[u];
        edge[tot].w=w;
        head[u]=tot;
    }
    void bfs() {
        queue<int>q;
        q.push(1);
        d[1]=1;
        while(!q.empty()) {
            int x=q.front();
            q.pop();
            for(int i=head[x]; i!=-1; i=edge[i].from) {
                int y=edge[i].to,w=edge[i].w;
                if(!d[y]) {
                    q.push(y);
                    d[y]=d[x]+1;
                    dis[y]=dis[x]+w;
                    fa[y][0]=x;
                    for(int j=1; j<=t; j++) {
                        fa[y][j]=fa[fa[y][j-1]][j-1];
                    }
                }
            }
        }
    }
    int lca(int x,int y) {
        if(d[x]>d[y]) swap(x,y);
        for(int i=t; i>=0; i--) {
            if(d[fa[y][i]]>=d[x]) {
                y=fa[y][i];
            }
        }
        if(x==y) return x;
        for(int i=t; i>=0; i--) {
            if(fa[x][i]!=fa[y][i]) {
                x=fa[x][i];
                y=fa[y][i];
            }
        }
        return fa[x][0];
    }
    int main() {
    //  ios::sync_with_stdio(false);
        //freopen("Distance Queries.in","r",stdin);
        memset(head,-1,sizeof(head));
        cin>>n>>m;
    //  t=(int)(log(n)/log(2))+1; //这两种写法写在poj会报如下错误
    //  t=(int)(log(double(n))/log(2))+1; //这两种写法写在poj会报如下错误
        /*
        F:\temp\20869669.63353\Main.cpp(81) : error C2668: 'log' : ambiguous call to overloaded function
            math.h(567): could be 'long double log(long double)'
            math.h(519): or       'float log(float)'
            math.h(121): or       'double log(double)'
            while trying to match the argument list '(int)'
        */ 
        t=int((log(double(n))/log(2.0)))+1;
        int u,v,w;
    //  char s[10];
        string s;
        for(int i=1; i<=m; i++) {
            scanf("%d%d%d",&u,&v,&w);
            scanf("%s",&s[0]);//这里不能写成 scanf("%s",s);
            add(u,v,w);
            add(v,u,w);
        }
        bfs();
        int q;
        cin>>q;
        while(q--){
            scanf("%d%d",&u,&v);
            printf("%d\n",dis[u]+dis[v]-2*dis[lca(u,v)]);
        }
        return 0;
    }
    #endif
    #ifdef method_2
    /*
    
    */
    
    #endif
    #ifdef method_3
    /*
    
    */
    
    #endif
    

    POJ 3728: The merchant
    因为存在买卖顺序的问题,问题不能单纯的理解为在树链上求解最值。
    考虑答案的产生过程,有三种情况。
    1 在from到lca的过程中完成买卖。
    2 在lca到to的过程中完成买卖。
    3 在from到lca的过程中以这一段的最低价格买入,在lca到to的过程中以这一段的最高价格卖出。
    因此将深度进行二进制划分,完成几个数组的dp。
    up数组,up[i][j]维护i到i节点往上2^j的节点的最大差价
    down数组,down[i][j]维护i节点往下2^j到i的节点的最大差价
    Max数组, Max[i][j]维护i到i节点往上2^j的节点之间价格的最大值
    Min数组,Min[i][j]维护i到i节点往上2^j的节点之间价格的最小值
    最后答案通过组合这些二进制数组得到。具体内容详见注释。本题在转移时细节较多,需要细心。
    代码如下

    /*
    
    */
    #define method_1
    #ifdef method_1
    /*
    因为存在买卖顺序的问题,问题不能单纯的理解为在树链上求解最值。
    考虑答案的产生过程,有三种情况。
    1 在from到lca的过程中完成买卖。
    2 在lca到to的过程中完成买卖。
    3 在from到lca的过程中以这一段的最低价格买入,在lca到to的过程中以这一段的最高价格卖出。
    因此将深度进行二进制划分,完成几个数组的dp。
    up数组,up[i][j]维护i到i节点往上2^j的节点的最大差价
    down数组,down[i][j]维护i节点往下2^j到i的节点的最大差价
    Max数组, Max[i][j]维护i到i节点往上2^j的节点之间价格的最大值
    Min数组,Min[i][j]维护i到i节点往上2^j的节点之间价格的最小值
    最后答案通过组合这些二进制数组得到。具体内容详见注释。本题在转移时细节较多,需要细心。 
    */
    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cmath>
    #include<set>
    #include<map>
    #include<queue>
    #include<stack>
    #include<vector>
    #include<cstring>
    #include<cstdlib>
    #define D(x) cout<<#x<<" = "<<x<<"  "
    #define E cout<<endl
    using namespace std;
    typedef long long ll;
    typedef pair<int,int>pii;
    const int maxn=50000+5;
    const int INF=0x3f3f3f3f;
    int n,q;
    struct node {
        int from,to;
    } edge[maxn<<1];
    int t,head[maxn],tot=0,fa[maxn][20],d[maxn],w[maxn],up[maxn][20],down[maxn][20],Max[maxn][20],Min[maxn][20];
    /*
    up数组,up[i][j]维护i到i节点往上2^j的节点的最大差价
    down数组,down[i][j]维护i节点往下2^j到i的节点的最大差价
    Max数组, Max[i][j]维护i到i节点往上2^j的节点之间价格的最大值
    Min数组,Min[i][j]维护i到i节点往上2^j的节点之间价格的最小值
    */
    void add(int u,int v) {
        edge[++tot].to=v;edge[tot].from=head[u];head[u]=tot;
    }
    void bfs() {
        queue<int>q;
        q.push(1);
        d[1]=1;
        while(!q.empty()) {
            int x=q.front();
            q.pop();
            for(int i=head[x]; i; i=edge[i].from) {
                int y=edge[i].to;
                if(!d[y]) {
                    q.push(y);
                    d[y]=d[x]+1;
                    Max[y][0]=max(w[x],w[y]);
                    Min[y][0]=min(w[x],w[y]);
                    up[y][0]=max(0,w[x]-w[y]);
    //              D(x);D(y);D(w[x]);D(w[y]);D(up[y][0]);E;
                    down[y][0]=max(0,w[y]-w[x]);
                    fa[y][0]=x;
                    int res1,res2;
                    for(int j=1; j<=t; j++) {
                        fa[y][j]=fa[fa[y][j-1]][j-1];
                        Max[y][j]=max(Max[y][j-1],Max[fa[y][j-1]][j-1]);
                        Min[y][j]=min(Min[y][j-1],Min[fa[y][j-1]][j-1]);
                        res1=max(0,Max[fa[y][j-1]][j-1]-Min[y][j-1]);
                        res2=max(up[fa[y][j-1]][j-1],up[y][j-1]);
    //                  D(y);D(j);D(res1);D(res2);D(Max[fa[y][j-1]][j-1]);D(Min[y][j-1]);E;
                        up[y][j]=max(res1,res2);
                        res1=max(0,Max[y][j-1]-Min[fa[y][j-1]][j-1]);
                        res2=max(down[fa[y][j-1]][j-1],down[y][j-1]);
                        down[y][j]=max(res1,res2);
                    }
                }
            }
        }
    }
    int lca(int x,int y) {
        if(d[x]>d[y]) swap(x,y);
        for(int i=t; i>=0; i--) {
            if(d[fa[y][i]]>=d[x]) {
                y=fa[y][i];
            }
        }
        if(x==y) return x;
        for(int i=t; i>=0; i--) {
            if(fa[x][i]!=fa[y][i]) {
                x=fa[x][i];
                y=fa[y][i];
            }
        }
        return fa[x][0];
    }
    int query_up(int x,int dep,int &val){ //val:x到lca的最大差值 
        val=0;
        int res=INF; //res:x到lca的最小值 
        for(int i=t;i>=0;i--){
            if(dep&(1<<i)){
                val=max(val,up[x][i]);
                val=max(val,Max[x][i]-res); //先算val保证此时的res表示的最小值一定在x以下(即对应点深度大于x) 
                res=min(res,Min[x][i]);
                x=fa[x][i];
            }
        }
        return res;
    }
    int query_down(int x,int dep,int &val){ //val:lca到x的最大差值 
        val=0;
        int res=0; //res:lca到x的最大值 
        for(int i=t;i>=0;i--){
            if(dep&(1<<i)){
                val=max(val,down[x][i]);
                val=max(val,res-Min[x][i]); //先算val保证此时的res表示的最小值一定在x以上(即对应点深度小于x) 
                res=max(res,Max[x][i]);
                x=fa[x][i];
            }
        }
        return res;
    }
    int main() {
    //  ios::sync_with_stdio(false);
        //freopen("The merchant.in","r",stdin);
        scanf("%d",&n);
        t=int((log(double(n))/log(2.0)))+1;
    //  D(t);E;
        for(int i=1;i<=n;i++) scanf("%d",&w[i]);
        int from,to;
        for(int i=1;i<=n-1;i++) scanf("%d%d",&from,&to),add(from,to),add(to,from);
        bfs();
        /* 
        for(int i=1;i<=n;i++){
            for(int j=0;j<=t;j++){
                D(i);D(j);D(Min[i][j]);D(Max[i][j]);D(up[i][j]);D(down[i][j]);E;
            }
            E;
        }
        */ 
        scanf("%d",&q);
        while(q--){
            scanf("%d%d",&from,&to);
            int temp=lca(from,to);
            int a,b,val1,val2;
            //a:from到lca的最小值
            //b:lca到to的最大值
            //val1:from到lca的最大差价
            //val2:lca到to的最大差价 
            a=query_up(from,d[from]-d[temp],val1);
            b=query_down(to,d[to]-d[temp],val2);
            int ans=max(0,max(max(val1,val2),b-a));
            printf("%d\n",ans);
        }
        return 0;
    }
    #endif
    #ifdef method_2
    /*
    
    */
    
    #endif
    #ifdef method_3
    /*
    
    */
    
    #endif
    

    相关文章

      网友评论

          本文标题:图论(二)

          本文链接:https://www.haomeiwen.com/subject/rcgcuctx.html