bzoj4025 二分图

其实直接lct完事了。。。

但是太暴力不好看。。。

 

每个边存在于一个时间区间

对于每个时间区间都有询问

线段树分治!

dfs最后扫一遍

并查集按秩合并!

奇环?

并查集树上每个边维护这个点到并查集父亲节点在真实树中的距离奇偶性

发现,这个奇偶性可以直接异或的(可以认为一条边走过两次就是没有走过)

所以直接更新即可。

用栈维护事情,连接两个点或者出现了奇环(这个只第一次放进去即可)

代码:

O(nlog^2n)但是比lct常数小

#include<bits/stdc++.h>
#define reg register int
#define il inline
#define mid ((l+r)>>1)
#define numb (ch^'0')
using namespace std;
typedef long long ll;
il void rd(int &x){
    char ch;x=0;bool fl=false;
    while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true);
    for(x=numb;isdigit(ch=getchar());x=x*10+numb);
    (fl==true)&&(x=-x);
}
namespace Miracle{
const int N=100000+5;
const int M=200000+5;
int n,m,T;
struct node{
    int x,y;
}b[M];
vector<int>mem[4*N];
int fa[N];
int sz[N];
int dis[N];
int fin(int x){
    return fa[x]==x?x:fin(fa[x]);
}
int dist(int x){
    return fa[x]==x?dis[x]:dis[x]^dist(fa[x]);
}
void upda(int x,int l,int r,int L,int R,int id){
    if(L<=l&&r<=R){
        mem[x].push_back(id);
        return;
    }
    if(L<=mid) upda(x<<1,l,mid,L,R,id);
    if(mid<R) upda(x<<1|1,mid+1,r,L,R,id);
}
bool fl;//is or not 
struct event{
    int id;
    int chan,to;
}sta[M];
int top;
void dfs(int x,int l,int r){
    int st=top;
    for(reg i=0;i<(int)mem[x].size();++i){
        int k1=fin(b[mem[x][i]].x),k2=fin(b[mem[x][i]].y);
        if(k1!=k2){
            if(sz[k1]>sz[k2]) swap(k1,k2);
            
            fa[k1]=k2;
            sz[k2]+=sz[k1];
            dis[k1]=dist(b[mem[x][i]].x)^dist(b[mem[x][i]].y)^1;
            
            sta[++top].id=mem[x][i];
            sta[top].chan=k1,sta[top].to=k2;
            
        }else if(fl){
            if((dist(b[mem[x][i]].x)^dist(b[mem[x][i]].y))==0){
                fl=false;
                ++top;
                sta[top].id=-1;
                sta[top].chan=0;
                sta[top].to=0;
            }
        }
    }
    if(l==r){
        puts(fl?"Yes":"No");
    }else{
        dfs(x<<1,l,mid);
        dfs(x<<1|1,mid+1,r);
    }
    while(top!=st){
        if(sta[top].id==-1){
            fl=true;
        }else{
            sz[sta[top].to]-=sz[sta[top].chan];
            fa[sta[top].chan]=sta[top].chan;
            dis[sta[top].chan]=0;
        }
        --top;
    }
}
int main(){
    rd(n);rd(m);rd(T);
    for(reg i=1;i<=n;++i) fa[i]=i,dis[i]=0,sz[i]=1;
    int l,r;
    for(reg i=1;i<=m;++i){
        rd(b[i].x);rd(b[i].y);
        rd(l);rd(r);
        ++l;//warning !!!
        if(l<=r)upda(1,1,T,l,r,i);
    }
    fl=true;
    dfs(1,1,T);
    return 0;
}

}
signed main(){
    Miracle::main();
    return 0;
}

/*
   Author: *Miracle*
   Date: 2019/2/10 20:59:09
*/

总结:
1.时间区间存在,可以想到线段树分治

2.并查集的维护距离的奇偶性的xor有点trick!

 

 

(这个题也可以对时间分治:[L,R]对于横跨[L,R]的边,拆成两个,分治下去。

但是如果到L=R的时候才停止的话,每个边拆成T个,就TLE了

发现,如果对于[l,r],如果一个边全程都出现在[l,r]中的话,那么就先加进去,就不拆开往下递归了

显然也是正确的

这样,每个边拆成log(T)份(类似线段树)

需要删除,并查集按秩合并(dis也是要维护的)

复杂度:O(mlog(T)log(N))

这个方法就是线段树分治。。。。

只不过线段是边分治边下放而已。

posted @ 2019-02-10 21:47  *Miracle*  阅读(472)  评论(0编辑  收藏  举报