Codeforces 576E

这种问题,可以很套路的用线段树分治+可回退并查集解决。对每种颜色维护一个可回退并查集,至于二分图的判定,将每条造成奇环的边打个标记就好了,回退的时候撤销一个标记,看一下是否存在标记就行了。
不过这题比较恶心的地方是某条边在某个时间的颜色不是一开始已知的,这个也不难解决,我们递归到叶子节点的时候判断一下是否能修改成功,然后再对下一段时间插入正确的颜色就好了。
时间复杂度 O ( n k + m log ⁡ 2 n ) \mathcal O(nk+m\log^2n) O(nk+mlog2n),因为要支持回退,所以不能路径压缩。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <utility>
#define FR first
#define SE second
#define last last2
 
using namespace std;
 
struct SETS;
typedef pair<SETS*,int> pr1;
typedef pair<int,int> pr2;
 
pr1 st[2000000];
int top;
 
struct SETS {
 
int fa[500005];
char rank[500005],d[500005];
 
int find_father(int x) {
  return (fa[x]==x)?x:find_father(fa[x]);
}
 
int find_dis(int x) {
  return (fa[x]==x)?0:d[x]^find_dis(fa[x]);
}
 
void merge(int x,int y) {
  int fx=find_father(x),fy=find_father(y);
  if (fx==fy) return;
  if (rank[fx]>rank[fy]) {
  	swap(x,y);
  	swap(fx,fy);
  }
  if (rank[fx]==rank[fy]) {
  	rank[fy]++;
    st[++top]=pr1(this,-fy);
  }
  d[fx]=find_dis(x)^find_dis(y)^1;
  fa[fx]=fy;
  st[++top]=pr1(this,fx);
}
 
void init(int n) {
  for(int i=1;i<=n;i++) fa[i]=i;
}
 
} s[51];
 
void ret(int last) {
  while (top>last) {
  	pr1 p=st[top--];
  	if (p.SE<0) (*p.FR).rank[-p.SE]--;
  	else (*p.FR).fa[p.SE]=p.SE;
  }
}
 
struct Edge {
  int s,t;
  Edge() {}
  Edge(int a,int b):s(a),t(b) {}
};
 
Edge e[500005];
vector<pr1> v[2000000];
 
void update(int l,int r,int o,int lx,int rx,pr1 now) {
  if (l>=lx&&r<=rx) v[o].push_back(now);
  else {
  	int m=((l+r)>>1);
  	if (m>=lx) update(l,m,o*2,lx,rx,now);
  	if (m<rx) update(m+1,r,o*2+1,lx,rx,now);
  }
}
 
pr1 q[500005];
bool ok[500005];
queue <int> mq[500005];
SETS* col[500005];
 
int n,m,k,que;
 
void solve(int l,int r,int o) {
  int last=top;
  for(int i=0;i<v[o].size();i++)
    (*v[o][i].FR).merge(e[v[o][i].SE].s,e[v[o][i].SE].t);
  if (l==r) {
  	SETS *d=q[l].FR;
  	int x=e[q[l].SE].s,y=e[q[l].SE].t;
  	if ((*d).find_father(x)!=(*d).find_father(y)||(*d).find_dis(x)^(*d).find_dis(y)) {
  		puts("YES");
  		col[q[l].SE]=d;
  		ok[q[l].SE]=true;
	  }
	else puts("NO");
	mq[q[l].SE].pop();
	if (ok[q[l].SE]&&l<que) update(1,que,1,l+1,mq[q[l].SE].front(),pr1(col[q[l].SE],q[l].SE));
  } 
  else {
  	int m=((l+r)>>1);
  	solve(l,m,o*2);
  	solve(m+1,r,o*2+1);
  }
  ret(last);
}
 
int main() {
  scanf("%d%d%d%d",&n,&m,&k,&que);
  for(int i=1;i<=k;i++) s[i].init(n);
  for(int i=1;i<=m;i++) {
  	int x,y;
  	scanf("%d%d",&x,&y);
  	e[i]=Edge(x,y);
  }
  for(int i=1;i<=que;i++) {
  	int x,y;
  	scanf("%d%d",&x,&y);
  	q[i]=pr1(&s[y],x);
  	mq[x].push(i);
  }
  for(int i=1;i<=m;i++) mq[i].push(que);
  solve(1,que,1);
  return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值