BZOJ P1924 [SDOI2010] 所驼门王的宝藏【缩点】【DAG上DP】【建图优化】

显然不能 n 2 n^2 n2建图,考虑建图优化。

对于横天门:

由于处于同一行的横天门一定属于同一个强连通分量,所以我们在建图连边的时候,只需要对在同一行的横天门构造一个环,而处于此行内的其他宫室,只需要从这个环中的任意一点向这个宫室建图连边就好了。

纵寰门同理。

另外一个什么什么门由于范围太小可以不管。

然后就直接缩点,跑最长链即可。

#include <bits/stdc++.h>
#define Make make_pair
#define Pair pair<int,int>
#define For(I,X,Y) for(int I=(X);I<=(Y);I++)
#define Fora(I,X,Y) for(int I=(X);I<(Y);I++)
#define ForDown(I,X,Y) for(int I=(X);I>=(Y);I--)
using namespace std;
  
const int Inf=1e9;
const int Max=1e5+5;
  
bool Vis[Max];
int Q,N,M,Ans,F[Max];
int Tot,Top,Num,Bel[Max],Low[Max],Dfn[Max],Size[Max],Stack[Max];
  
int Dx[8]={-1,-1,-1,0,0,1,1,1};
int Dy[8]={-1,0,1,-1,1,-1,0,1};
 
stack<int>S;
map<Pair,int>Map;
 
struct Node{
    int X,Y,Id,Type;
}Door[Max];
 
struct Graph{
    int Cnt,To[Max*10],Next[Max*10],Head[Max];
  
    void Insert(int X,int Y) {
        To[++Cnt]=Y;Next[Cnt]=Head[X];Head[X]=Cnt;
    }
}G1,G2;
  
inline int Read(){
    int X=0;char CH=getchar();bool F=0;
    while(CH>'9'||CH<'0'){if(CH=='-')F=1;CH=getchar();}
    while(CH>='0'&&CH<='9'){X=(X<<1)+(X<<3)+CH-'0';CH=getchar();}
    return F?-X:X;
}
 
bool Cmp1(Node P,Node Q){
    if(P.X!=Q.X) return P.X<Q.X;
    if(P.Type==1) return true;
    if(Q.Type==1) return false;return P.Y<Q.Y;
}
 
bool Cmp2(Node P,Node Q){
    if(P.Y!=Q.Y) return P.Y<Q.Y;
    if(P.Type==2) return true;
    if(Q.Type==2) return false;return P.X<Q.X;
}
 
void DP(int X){
    Vis[X]=1;
    for(int I=G2.Head[X];I;I=G2.Next[I]) {
        if(!Vis[G2.To[I]]) DP(G2.To[I]);
          
        F[X]=max(F[X],F[G2.To[I]]);
    }
      
    Ans=max(Ans,F[X]+=Size[X]);
}
  
void Tarjan(int X){
    S.push(X);
    Dfn[X]=Low[X]=++Num;
      
    for(int I=G1.Head[X];I;I=G1.Next[I]){
        int Y=G1.To[I];
        if(!Dfn[Y]){
            Tarjan(Y);
            Low[X]=min(Low[X],Low[Y]);
        } else if(!Bel[Y]){
            Low[X]=min(Low[X],Dfn[Y]);
        }
    }
  
    if(Dfn[X]==Low[X]){
        ++Tot;
        while(true){
            int Y=S.top();
            Size[Tot]++;Bel[Y]=Tot;S.pop();
            if(X==Y) break;
        }
    }
}
  
int main(){
    Q=Read(),N=Read(),M=Read();
  
    For(I,1,Q) {
        Door[I].Id=I;
        Door[I].X=Read();
        Door[I].Y=Read();
        Door[I].Type=Read();Map[Make(Door[I].X,Door[I].Y)]=I;
    }
     
    sort(Door+1,Door+1+Q,Cmp1);
     
    int First=1,Last=1;
     
    For(I,1,Q) {
        if(Door[I].X!=Door[I+1].X) {
            if(Last!=First) G1.Insert(Door[Last].Id,Door[First].Id);Last=First=I+1;
        } else {
            if(Door[Last].Type==1) G1.Insert(Door[Last].Id,Door[I+1].Id);
            if(Door[I+1].Type==1) Last=I+1;
            if(Door[First].Type!=1) Last=First=I+1;
        }
    }
     
    sort(Door+1,Door+1+Q,Cmp2);
     
    First=1,Last=1;
     
    For(I,1,Q) {
        if(Door[I].Y!=Door[I+1].Y) {
            if(Last!=First) G1.Insert(Door[Last].Id,Door[First].Id);Last=First=I+1;
        } else {
            if(Door[Last].Type==2) G1.Insert(Door[Last].Id,Door[I+1].Id);
            if(Door[I+1].Type==2) Last=I+1;
            if(Door[First].Type!=2) Last=First=I+1;
        }
    }
     
    For(I,1,Q) {
        if(Door[I].Type==3) {
            int X=Door[I].X,Y=Door[I].Y;
            Fora(J,0,8) {
                if(Map.count(Make(X+Dx[J],Y+Dy[J]))) {
                    G1.Insert(Door[I].Id,Map[Make(X+Dx[J],Y+Dy[J])]);
                } 
            }
        }
    }
     
    For(I,1,Q) if(!Dfn[I]) Tarjan(I);
  
    For(I,1,Q) for(int J=G1.Head[I];J;J=G1.Next[J]) if(Bel[I]!=Bel[G1.To[J]]) G2.Insert(Bel[I],Bel[G1.To[J]]);
  
    For(I,1,Tot) if(!Vis[I]) DP(I);
  
    printf("%d",Ans);
  
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值