图论(二)

强连通分量

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
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,546评论 6 507
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,224评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,911评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,737评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,753评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,598评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,338评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,249评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,696评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,888评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,013评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,731评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,348评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,929评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,048评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,203评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,960评论 2 355

推荐阅读更多精彩内容