bzoj1018 [SHOI2008]堵塞的交通traffic

传送门

恶心的线段树。。。
吐槽:这题update真tm难写……
线段树维护连通性以前听说过,但这一次是第一次写,结果写吐了。。
由于城市中道路是2行n列,所以我们可以在线段树中维护左边两个点和右边两个点以及左边两个点之间和右边两个点之间的连通性。横着的操作可以在线段树递归到这条边连接左右子树的时候进行操作,竖着的操作可以在递归到叶子的时候将一对点看作两对点,然后将其位于对角线上的两对点以及左边一对和右边一对的连通性修改,其余的注意update就行了。

CODE:

#include<cstdio>
#define N 100005
struct tree
{
    bool a[2][2];
    bool left,right;
    bool up,down;
}t[N<<2];
char str[10];
int n,x1,y1,x2,y2;
inline void swap(int &a,int &b){a^=b,b^=a,a^=b;}
inline bool getstring()
{
    char c=getchar();int p=0;
    while((c<'A'||c>'Z')&&(c<'a'||c>'z')) c=getchar();
    while((c>='A'&&c<='Z')||(c>='a'&&c<='z')) str[++p]=c,c=getchar();
    return str[1]!='E';
}
inline void update(tree &now,tree &s1,tree &s2)
{
    now.a[0][0]=(s1.a[0][0]&&now.up&&s2.a[0][0])||(s1.a[0][1]&&now.down&&s2.a[1][0]);
    now.a[0][1]=(s1.a[0][0]&&now.up&&s2.a[0][1])||(s1.a[0][1]&&now.down&&s2.a[1][1]);
    now.a[1][0]=(s1.a[1][0]&&now.up&&s2.a[0][0])||(s1.a[1][1]&&now.down&&s2.a[1][0]);
    now.a[1][1]=(s1.a[1][0]&&now.up&&s2.a[0][1])||(s1.a[1][1]&&now.down&&s2.a[1][1]);
    now.left =s1.left ||(s1.a[0][0]&&s1.right&&s1.a[1][1])||(s1.a[0][0]&&now.up&&s2.left &&now.down&&s1.a[1][1])||(s1.a[0][0]&&now.up&&s2.a[0][0]&&s2.right&&s2.a[1][1]&&now.down&&s1.a[1][1]);
    now.right=s2.right||(s2.a[0][0]&&s2.left &&s2.a[1][1])||(s2.a[0][0]&&now.up&&s1.right&&now.down&&s2.a[1][1])||(s2.a[0][0]&&now.up&&s1.a[0][0]&&s1.left &&s1.a[1][1]&&now.down&&s2.a[1][1]);
}
void build(int l,int r,int now)
{
    if(l==r)
    {
        t[now].a[0][0]=t[now].a[1][1]=1;
        return;
    }
    int mid=(l+r)>>1;
    build(l,mid,now<<1);
    build(mid+1,r,now<<1|1);
}
void add1(int l,int r,int now,bool change)
{
    int mid=(l+r)>>1;
    if(y1==mid)
    {
        if(x1==1) t[now].up=change;
        else t[now].down=change;
        update(t[now],t[now<<1],t[now<<1|1]);
        return;
    }
    if(y1<mid) add1(l,mid,now<<1,change);
    else add1(mid+1,r,now<<1|1,change);
    update(t[now],t[now<<1],t[now<<1|1]);
}
void add2(int l,int r,int now,bool change)
{
    if(l==r)
    {
        t[now].a[0][1]=t[now].a[1][0]=change;
        t[now].left=t[now].right=change;
        return;
    }
    int mid=(l+r)>>1;
    if(y1<=mid) add2(l,mid,now<<1,change);
    else add2(mid+1,r,now<<1|1,change);
    update(t[now],t[now<<1],t[now<<1|1]);
}
tree ask(int L,int R,int l,int r,int now)
{
    if(L>R||L<0||R>n) return t[0];
    if(L<=l&&r<=R) return t[now];
    int mid=(l+r)>>1;
    if(R<=mid) return ask(L,R,l,mid,now<<1);
    if(L>mid) return ask(L,R,mid+1,r,now<<1|1);
    tree ans=t[now],tmp1,tmp2;
    tmp1=ask(L,R,l,mid,now<<1);
    tmp2=ask(L,R,mid+1,r,now<<1|1);
    update(ans,tmp1,tmp2);
    return ans;
}
inline void change(bool wh)
{
    if(x1==x2)
    {
        if(y1>y2) swap(y1,y2);
        return add1(1,n,1,wh);
    }
    if(x1>x2) swap(x1,x2);
    add2(1,n,1,wh);
}
inline bool Ask()
{
    if(y1>y2) swap(x1,x2),swap(y1,y2);
    tree mid=ask(y1,y2,1,n,1);
    int wh1=x1==1?0:1,wh2=x2==1?0:1;
    if(mid.a[wh1][wh2]) return 1;
    tree left=ask(1,y1-1,1,n,1),right=ask(y2+1,n,1,n,1);
    tree llink=ask(y1-1,y1,1,n,1),rlink=ask(y2,y2+1,1,n,1);
    if(!mid.left) mid.left=(llink.a[0][0]&&llink.a[1][1]&&left.right);
    if(!mid.right) mid.right=(rlink.a[0][0]&&rlink.a[1][1]&&right.left);
    if(!wh1&&!wh2) return mid.a[0][0]||(mid.left&&mid.a[1][1]&&mid.right);
    if(!wh1&&wh2) return (mid.a[0][0]&&mid.right)||(mid.left&&mid.a[1][1]);
    if(wh1&&!wh2) return (mid.left&&mid.a[0][0])||(mid.a[1][1]&&mid.right);
    return mid.a[1][1]||(mid.left&&mid.a[0][0]&&mid.right);
}
int main()
{
    scanf("%d",&n);
    build(1,n,1);
    while(getstring())
    {
        scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
        if(str[1]=='O') change(1);
        else if(str[1]=='C') change(0);
        else if(Ask()) puts("Y");
        else puts("N");
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值