点此看题
把每条边出现时间段打到线段树上面,然后跑一遍线段树。
问题在于维护一个树的结构,如果一条边连接的两点暂时还不连通,我们就连接一波。否则我们看这条非树边构成的环是不是奇环,如果是的话直接不符合条件,否则没有影响(这里你需要考虑两条非树边构成的环)
可以用启发式合并的并查集,可以算一个点到根的距离的奇偶 d i s dis dis,合并 ( u , v ) (u,v) (u,v)需要连接一条 d i s ( u ) ⊕ d i s ( v ) ⊕ 1 dis(u)\oplus dis(v)\oplus 1 dis(u)⊕dis(v)⊕1,因为我们是合并的根,你可以发现这样构建的两点距离是 1 1 1 。回退的时候记录一下哪两个点合并,撤销就很容易了。
#include
#include
#include
#include
using namespace std;
#define mk make_pair
#define pii pair
const int M = 100005;
int read()
{
int x=0,flag=1;char c;
while((c=getchar())<'0' || c>'9') if(c=='-') flag=-1;
while(c>='0' && c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
return x*flag;
}
int n,m,tp,dep[M],c[M],fa[M],st[2*M];
map<pii,int> mp;vector<pii> v[4*M];
int find(int x)
{
return x==fa[x]?x:find(fa[x]);
}
int dis(int x)
{
return x==fa[x]?0:(c[x]^dis(fa[x]));
}
void merge(int u,int v,int ct)
{
if(dep[u]<dep[v]) swap(u,v);
fa[v]=u;c[v]=ct;
if(dep[u]==dep[v]) st[++tp]=-u,dep[u]++;
st[++tp]=v;
}
void back(int t)
{
while(tp>t)
{
if(st[tp]<0) dep[-st[tp]]--;
else fa[st[tp]]=st[tp],c[st[tp]]=0;
tp--;
}
}
void ins(int i,int l,int r,int L,int R,pii x)
{
if(l>R || L>r) return ;
if(L<=l && r<=R)
{
v[i].push_back(x);
return ;
}
int mid=(l+r)>>1;
ins(i<<1,l,mid,L,R,x);
ins(i<<1|1,mid+1,r,L,R,x);
}
void ask(int i,int l,int r)
{
int cur=tp;
for(int j=0;j<v[i].size();j++)
{
int x=v[i][j].first,y=v[i][j].second;
int flag=dis(x)^dis(y)^1;
if(find(x)==find(y))
{
if(flag)
{
for(int i=l;i<=r;i++)
puts("NO");
back(cur);
return ;
}
}
else merge(find(x),find(y),flag);
}
if(l==r)
{
puts("YES");
back(cur);
return ;
}
int mid=(l+r)>>1;
ask(i<<1,l,mid);
ask(i<<1|1,mid+1,r);
back(cur);
}
int main()
{
n=read();m=read();
for(int i=1;i<=n;i++)
fa[i]=i;
for(int i=1;i<=m;i++)
{
int u=read(),v=read();pii t=mk(u,v);
if(mp.count(t))
ins(1,1,m,mp[t],i-1,t),mp.erase(t);
else mp[t]=i;
}
for(map<pii,int>::iterator it=mp.begin();it!=mp.end();it++)
ins(1,1,m,it->second,m,it->first);
ask(1,1,m);
}