[bzoj1018]:[SHOI2008]堵塞的交通traffic

传送门
我自从月考完了,就在调这个题。。
首先,先看一下官方题解。
算法:线段树
先贴一个连接:https://www.luogu.org/problemnew/show/U16516
这个是分测试点,可以看自己对了多少分。
然后开始算法解释。
一部分一部分来。


这里是data的声明,初始化(很重要),还有输出调试。
data里的l,r代表所管理的区间
数组a代表存储的联通信息
具体联通信息所代表的含义如下图:
这里写图片描述
其中圆中的数字代表节点编号
边上的数字代表连通性标号

const int N=1e5+5;
struct data{
    int l,r;
    int a[7];
    data(){
        l=r=0;
        memset(a,0,sizeof(a));
    }
}f[N];
inline void print(const data& x){
    cout<<endl;
    cout<<"data of "<<x.l<<" to "<<x.r<<endl;
    char s[10]="f[ ] = ";
    for(int i=1;i<=6;i++)s[2]=i+'0',printf("%s%d\n",s,x.a[i]);
    cout<<endl;
}

这里是将边初始化到邻接矩阵里,然后跑floyd

int n;
int g[5][5],e1[N],e2[N],e3[N];//g为邻接矩阵,e1一排横边,e2二排横边,e3竖边
inline void init(int x){
    memset(g,0,sizeof(g));
    if(e1[x])g[1][2]=g[2][1]=1;
    if(e2[x])g[3][4]=g[4][3]=1;
    if(e3[x])g[1][3]=g[3][1]=1;
    if(e3[x+1])g[2][4]=g[4][2]=1;
}
inline void floyd(){
    for(int k=1;k<=4;k++)
        for(int i=1;i<=4;i++)
            for(int j=1;j<=4;j++)
                g[i][j]|=g[i][k]&g[k][j];
}

这是利用邻接矩阵初始化一个data信息
第二个merge是合并两个data

inline void color(data &x){
    x.a[1]=g[1][3];
    x.a[2]=g[1][2];
    x.a[3]=g[2][4];
    x.a[4]=g[3][4];
    x.a[5]=g[1][4];
    x.a[6]=g[2][3];
}
inline data merge(const data& a,const data& b){
    if(!a.l)return b;
    if(!b.l)return a;
    data c;
    c.l=a.l;c.r=b.r;
    c.a[1]=a.a[1]|(a.a[2]&b.a[1]&a.a[4]);
    c.a[2]=(a.a[2]&b.a[2])|(a.a[5]&b.a[6]);
    c.a[3]=b.a[3]|(b.a[2]&a.a[3]&b.a[4]);
    c.a[4]=(a.a[4]&b.a[4])|(a.a[6]&b.a[5]);
    c.a[5]=(a.a[2]&b.a[5])|(a.a[5]&b.a[4]);
    c.a[6]=(a.a[4]&b.a[6])|(a.a[6]&b.a[2]);
    return c;
}

以下是线段树的pushup和build

inline void pushup(int rt){
    f[rt]=merge(f[rt<<1],f[rt<<1|1]);
}
inline void build(int rt,int l,int r){
    f[rt].l=l;f[rt].r=r;
    if(l==r){
        init(l);
        floyd();
        color(f[rt]);
        return;
    }
    int mid=(l+r)>>1;
    build(rt<<1,l,mid);
    build(rt<<1|1,mid+1,r);
    pushup(rt);
}

以下是线段树的update和query

int L,R,pos;
inline void update(int rt,int l,int r){
    if(l==r){
        init(l);
        floyd();
        color(f[rt]);
        return;
    }
    int mid=(l+r)>>1;
    if(pos<=mid)update(rt<<1,l,mid);
    else update(rt<<1|1,mid+1,r);
    pushup(rt);
}
data all;//all代表全联通data,后面有用
inline data query(int rt,int l,int r){
    if(L<=l&&r<=R)return f[rt];
    int mid=(l+r)>>1;
    data ans;
    if(L<=mid)ans=merge(ans,query(rt<<1,l,mid));
    if(mid+1<=R)ans=merge(ans,query(rt<<1|1,mid+1,r));
    // cout<<"ask of "<<l<<" to "<<r<<endl;
    // print(ans);
    return ans;
}

以下为主程序的一部分

int main(){
    for(int i=1;i<=6;i++)all.a[i]=1;
    n=read()-1;
    build(1,1,n);
    int X1,Y1,X2,Y2;
    while(1){
        char opt[10];
        scanf("%s",opt);
        if(opt[0]=='E')break;
        X1=read();Y1=read();X2=read();Y2=read();
        /*
        some code
        */
    }
    return 0;
}

以下为Open操作:

if(opt[0]=='O'){
    if(X1!=X2){
        e3[Y1]=1;
        pos=Y1,update(1,1,n);
        if(Y1>1)pos=Y1-1,update(1,1,n);
    }
    else{
        if(Y1>Y2)swap(Y1,Y2);
        if(X1==1)e1[Y1]=1;
        else e2[Y1]=1;
        pos=Y1,update(1,1,n);
    }
}

接下来是Close操作,与Open类似:

else if(opt[0]=='C'){
    if(X1!=X2){
        e3[Y1]=0;
        pos=Y1,update(1,1,n);
        if(Y1>1)pos=Y1-1,update(1,1,n);
    }
    else{
        if(Y1>Y2)swap(Y1,Y2);
        if(X1==1)e1[Y1]=0;
        else e2[Y1]=0;
        pos=Y1,update(1,1,n);
    }
}

然后是Ask,超级长(为了避免错误,进行了许多讨论):

else{
    bool flag=0;
    if(Y1==Y2){
        if(X1==X2)flag=1;
        else{
            if(Y1==1){
                L=1,R=n;
                data ans=query(1,1,n);
                if(ans.a[1])flag=1;
            }
            else if(Y1==n+1){
                L=1,R=n;
                data ans=query(1,1,n);
                if(ans.a[3])flag=1;
            }
            else{
                L=1,R=Y1-1;
                data ans1=query(1,1,n);
                if(ans1.a[3])flag=1;
                L=Y1,R=n;
                data ans2=query(1,1,n);
                if(ans2.a[1])flag=1;
            }
        }
    }
    else{
        if(Y1>Y2)swap(Y1,Y2),swap(X1,X2);
        L=Y1,R=Y2-1;
        data ans=query(1,1,n);
        if(X1==1&&X2==1)flag=ans.a[2];
        else if(X1==1&&X2==2)flag=ans.a[5];
        else if(X1==2&&X2==1)flag=ans.a[6];
        else flag=ans.a[4];
        data ans1,ans2;
        L=1,R=Y1-1;
        if(L<=R)ans1=query(1,1,n);
        else ans1=all;
        L=Y2,R=n;
        if(L<=R)ans2=query(1,1,n);
        else ans2=all;
        if(X1==1&&X2==1)flag|=ans.a[4]&ans1.a[3]&ans2.a[1];
        else if(X1==1&&X2==2)flag|=ans.a[6]&ans1.a[3]&ans2.a[1];
        else if(X1==2&&X2==1)flag|=ans.a[5]&ans1.a[3]&ans2.a[1];
        else flag|=ans.a[2]&ans1.a[3]&ans2.a[1];
    }
    if(flag)puts("Y");
    else puts("N");
}

这个题就是个代码题,太难调了。。
还有线段树真的好神呀!
完结撒花!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值