蓝书(算法竞赛进阶指南)刷题记录——POJ1733 Parity game(并查集)

题目:POJ1733.
题目大意:给定 n n n个区间 [ l i , r i ] [l_i,r_i] [li,ri] a i a_i ai,表示 [ l i , r i ] [l_i,r_i] [li,ri]的权值和为奇数或偶数,问到哪一个区间不矛盾,但它的下一个区间矛盾.
1 ≤ n ≤ 5 ∗ 1 0 3 1\leq n\leq 5*10^3 1n5103.

容易想到前缀和,那么区间就变成了两个点 l i − 1 l_i-1 li1 r i r_i ri上的信息.

考虑用带权并查集维护每个点的前缀和 d [ i ] d[i] d[i],一个点 i i i的点权表示 i i i的前缀和异或 i i i的父亲的点权,即区间 [ f a [ i ] + 1 , i ] [fa[i]+1,i] [fa[i]+1,i]的奇偶性.

加入一个区间 [ l i , r i ] [l_i,r_i] [li,ri]时若 l i − 1 , r i l_i-1,r_i li1,ri在同一个区间就大力判断它们点权异或是否等于 a [ i ] a[i] a[i].然后加入这个区间就相当于把 r i r_i ri的祖先设为 l i − 1 l_i-1 li1,并同时更新点权.

注意这个时候路径压缩时也要更新点权.

时间复杂度 O ( n log ⁡ n ) O(n\log n) O(nlogn).

代码如下:

#include<iostream>
#include<cstdio>
#include<algorithm>
  using namespace std;

#define Abigail inline void
typedef long long LL;

const int N=5000;

int ri(){
  char c=getchar();
  int x=0,y=1;
  for (;c<'0'||c>'9';c=getchar()) if (c=='-') y=-1;
  for (;c<='9'&&c>='0';c=getchar()) x=x*10+c-'0';
  return x*y;
}

int rc(){
  char c=getchar();
  while (c<'a'||c>'z') c=getchar();
  return c=='o'?1:0;
}

int n,ord[N*2+9],x[N+9],y[N+9],a[N+9];
int fa[N*2+9],d[N*2+9],ans;

int lower(int k){
  int l=1,r=n<<1,mid=l+r>>1;
  for (;l<r;mid=l+r>>1)
    k<=ord[mid]?r=mid:l=mid+1;
  return l;
}

int get(int u){
  if (u==fa[u]) return u;
  int rot=get(fa[u]);d[u]^=d[fa[u]];
  return fa[u]=rot;
}

Abigail into(){
  ri();n=ri();
  for (int i=1;i<=n;++i){
  	x[i]=ri()-1;y[i]=ri();a[i]=rc();
  	ord[i*2-1]=x[i];ord[i*2]=y[i];
  }
}

Abigail work(){
  sort(ord+1,ord+1+2*n);
  for (int i=0;i<=n;++i)
    x[i]=lower(x[i]),y[i]=lower(y[i]);
  for (int i=1;i<=n<<1;++i) fa[i]=i;
  int u,v;
  ans=n;
  for (int i=1;i<=n;++i){
  	u=get(x[i]);v=get(y[i]);
  	if (u==v&&d[x[i]]^d[y[i]]^a[i]){ans=i-1;return;}
  	fa[v]=u,d[v]=d[x[i]]^d[y[i]]^a[i];
  }
}

Abigail outo(){
  printf("%d\n",ans);
}

int main(){
  into();
  work();
  outo();
  return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值