【20200203】【lyk】CR616 1C Prefix Enlightenment 题解

题意

链接:http://codeforces.com/contest/1290/problem/C
n(3e5)个灯,开始有亮有灭,k(3e5)个集合,每次操作可以改变某个集合内所有灯的状态。保证任意三个集合之交为空,问使1、12、123、…、1-n这n种组合的灯同时亮分别至少需要多少次操作(题目保证存在一种操作方案使所有灯同时亮)

思路

三个集合交为空的条件易转化为每个灯最多只包含在两个集合中。考虑把集合作为点,灯作为边,用点的二染色代表是否操作某个集合,白代表选、黑代表不选。则原来亮的灯表示边的两端点同色,黑的边表示两端点异色。由于题目保证存在合法染色,只需要用并查集维护一下就可了

比赛的时候看到异或、亮灭两个元素就死命想2-sat了。。。遂卒。那么问题来了,2-sat资不资持动态加边?




#include <stdio.h>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <algorithm>
 
using namespace std;
int n,k,a[302020],m,aa,b[303030],size[303003],sum[303030],f[303030],cho[303030],ans = 0,con[303030],col[303030],s[303030];
char S[303030];
bool fix[303030];
inline int getnum(int x) {
 if (fix[x])
  return con[x]?size[x]-sum[x]:sum[x];
 return min(sum[x],size[x]-sum[x]);
}
inline int getf(int x) {
 if (f[x] == x)
  return x;
 int tmp = getf(f[x]);
 col[x] = col[x]^col[f[x]];
 f[x] = tmp;
 return f[x];
}
int main() {
 scanf("%d%d",&n,&k);
 scanf("%s",S+1);
 for (int i = 1;i <= n; i++)
  s[i] = S[i]-'0';
 for (int i = 1;i <= k; i++) {
  f[i] = i;
  size[i] = sum[i] = 1;
  cho[i] = 1;
  col[i] = 0;
  scanf("%d",&m);
  for (int j = 1;j <= m; j++) {
   scanf("%d",&aa);
   if (!a[aa])
    a[aa] = i;
   else
    b[aa] = i;
  }
 }
 for (int i = 1;i <= n; i++) {
  if (!b[i]) {
   int fa = getf(a[i]);
   ans -= getnum(fa);
   if (s[i])
    con[fa] = col[a[i]]^1;
   else
    con[fa] = col[a[i]];
   fix[fa] = 1;
   ans += getnum(fa);
  }
  else if (a[i] && b[i]) {
   int fa = getf(a[i]),fb = getf(b[i]);
   if (fa != fb) {
    ans -= getnum(fa);
    ans -= getnum(fb);
    size[fb] += size[fa];
    f[fa] = fb;
    col[fa] = col[a[i]]^col[b[i]]^s[i]^1;
    if (fix[fa])
     con[fb] = col[fa]^con[fa],fix[fb] = 1;
    if (col[fa])
     sum[fb] += size[fa]-sum[fa];
    else
     sum[fb] += sum[fa];
    ans += getnum(fb);
   }
  }
  printf("%d\n",ans);
 }
 return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值