「POI2011 R1」Conspiracy

解题思路 :

问题转化为,将点集分成两部分,其中一部分恰好组成一个团,其中另一部分恰好组成一个独立集。

观察发现,如果求出了一个解,那么答案最多可以在这个解的基础上将一个点从团移到独立集,一个点从独立集移到团。

证明,如果有两个点从团移到独立集,那么这两个点之间的边就矛盾了,如果有两个点从独立集移到团,那么这两个点之间没有边也矛盾了。

所以只要我们求出了任意一组解,我们就可以通过枚举哪个点从团内移出去和哪个点从独立集里移进来来求出解的数量。

再进一步观察,发现这个模型可以用2-sat来建图,把每个点 \(x\) 拆成 \(x\) 和 \(x'\) 。表示 \(x\) 在团里和 \(x\) 在独立集里,如果 \(x,y\) 之间有边,那么如果 \(x\) 选了独立集 \(y\) 就必须要选团,所以连一条 \(x'\rightarrow y\) 。同理,如果 \(x, y\) 之间没有边,那么如果 \(x\) 选了团 \(y\) 就必须要选独立集,所以连一条 \(x\rightarrow y'\) 。最后 2-sat求出一组解即可,总复杂度 \(O(n^2)\)。

code

/*program by mangoyang*/
#pragma GCC optimize("Ofast","inline")
#include<bits/stdc++.h>
#define inf (0x7f7f7f7f)
#define Max(a, b) ((a) > (b) ? (a) : (b))
#define Min(a, b) ((a) < (b) ? (a) : (b))
typedef long long ll;
using namespace std;
template <class T>
inline void read(T &x){
int f = 0, ch = 0; x = 0;
for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = 1;
for(; isdigit(ch); ch = getchar()) x = x * 10 + ch - 48;
if(f) x = -x;
} const int N = 5005; bitset<N>mp[N];
short t[2][N], tot[2], deg[N], n; int ans; namespace twosat{
queue<short> q;
bitset<N<<1>mp[N<<1];
vector<short> d[N*2], c[N*2], g[N*2];
short st[N*2], dfn[N*2], ins[N*2], deg[N*2];
short in[N*2], low[N*2], col[N*2], top, id, Index;
inline void spop(int u, int id){
ins[u] = 0, col[u] = id, c[id].push_back(u);
for(int i = 0; i < g[u].size(); i++)
d[id].push_back(g[u][i]);
}
inline void tarjan(int u){
dfn[u] = low[u] = ++Index, st[++top] = u, ins[u] = 1;
for(int i = 0; i < g[u].size(); i++){
int v = g[u][i];
if(!dfn[v]) tarjan(v), low[u] = min(low[v], low[u]);
else if(ins[v]) low[u] = min(dfn[v], low[u]);
}
if(dfn[u] == low[u]){
++id;
while(st[top] != u) spop(st[top--], id);
spop(st[top--], id);
}
}
inline void solve(){
for(int i = 1; i <= 2 * n; i++) if(!dfn[i]) tarjan(i);
for(int i = 1; i <= n; i++)
if(col[i] == col[i+n]){ puts("0"), exit(0); }
for(int i = 1; i <= id; i++)
for(int j = 0; j < d[i].size(); j++)
if(col[d[i][j]] != i && !mp[col[d[i][j]]][i])
mp[col[d[i][j]]][i] = 1, deg[i]++;
for(int i = 1; i <= id; i++) if(!deg[i]) q.push(i);
for(; !q.empty(); q.pop()){
int u = q.front();
for(int i = 0; i < c[u].size(); i++){
int v = c[u][i];
int pos = v <= n ? 0 : 1, v1 = v <= n ? v + n : v - n;
if(!in[v1])
t[pos][++tot[pos]] = v <= n ? v : v - n, in[v] = 1;
}
for(int i = 1; i <= id; i++)
if(mp[u][i] && !--deg[i]) q.push(i);
}
}
} int main(){
read(n);
for(int i = 1, num, x; i <= n; i++){
read(num);
for(int j = 1; j <= num; j++) read(x), mp[i][x] = 1;
}
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n; j++) if(i != j){
if(mp[i][j]) twosat::g[i+n].push_back(j), deg[i]++;
else twosat::g[i].push_back(j+n);
}
twosat::solve();
if(tot[0] && tot[1]) ans++;
if(tot[0] > 1)
for(int i = 1; i <= tot[0]; i++)
if(deg[t[0][i]] == tot[0] - 1) ans++;
for(int i = 1; i <= tot[1]; i++){
int flag = 1, cnt = 1, u = 0;
for(int j = 1; j <= tot[0]; j++)
if(!mp[t[1][i]][t[0][j]]){
if(++cnt > 1){ flag = 0; break; }
u = t[0][j];
}
if(flag && !u && tot[1] > 1) ans++;
if(flag && u && deg[u] == tot[0]) ans++;
}
cout << ans;
return 0;
}

「POI2011 R1」Conspiracy的更多相关文章

  1. LuoguP7127 「RdOI R1」一次函数(function) 题解

    Content 设 \(S_k\) 为直线 \(f(x)=kx+k-1\),直线 \(f(x)=(k+1)x+k\) 与 \(x\) 轴围成的三角形的面积.现在给出 \(t\) 组询问,每组询问给定一 ...

  2. 「POI2011」Meteors

    「POI2011」Meteors 传送门 整体二分,树状数组实现区间修改单点查询,然后注意修改是在环上的. 参考代码: #include <cstdio> #include <vec ...

  3. 「AHOI / HNOI2017」影魔

    「AHOI / HNOI2017」影魔 题目描述 解决这类比较复杂的区间贡献问题关键在于找到计算的对象. 比如这道题,我们计算的对象就是区间中间的最大值. 对于点\(i\),我们找到左边第一个比他大的 ...

  4. [LOJ 2022]「AHOI / HNOI2017」队长快跑

    [LOJ 2022]「AHOI / HNOI2017」队长快跑 链接 链接 题解 不难看出,除了影响到起点和终点的射线以外,射线的角度没有意义,因为如果一定要从该射线的射出一侧过去,必然会撞到射线 因 ...

  5. 「国家集训队」小Z的袜子

    「国家集训队」小Z的袜子 传送门 莫队板子题. 注意计算答案的时候,由于分子分母都要除以2,所以可以直接约掉,这样在开桶算的时候也方便一些. 参考代码: #include <algorithm& ...

  6. P7708「Wdsr-2.7」八云蓝自动机 Ⅰ

    *X. P7708「Wdsr-2.7」八云蓝自动机 Ⅰ. 摘自 分治与根号数据结构学习笔记 第三部分 莫队 例题 X.. 一道莫队好题.私以为本题最有价值的地方在于对单点修改的转化以及对交换两个数的处 ...

  7. Note -「动态 DP」学习笔记

    目录 「CF 750E」New Year and Old Subsequence 「洛谷 P4719」「模板」"动态 DP" & 动态树分治 「洛谷 P6021」洪水 「S ...

  8. 前端构建工具之gulp(一)「图片压缩」

    前端构建工具之gulp(一)「图片压缩」 已经很久没有写过博客了,现下终于事情少了,开始写博吧 今天网站要做一些优化:图片压缩,资源合并等 以前一直使用百度的FIS工具,但是FIS还没有提供图片压缩的 ...

  9. fir.im Weekly - 如何打造 Github 「爆款」开源项目

    最近 Android 转用 Swift 的传闻甚嚣尘上,Swift 的 Github 主页上已经有了一次 merge>>「Port to Android」,让我们对 Swift 的想象又多 ...

随机推荐

  1. js学习阶段总结

    typeof操作符:返回字符串,可能是“undefined”,“boolean”,“ string”,“number”,“object”,“function”中的一种,所以不能判断数组. NaN(No ...

  2. 【leetcode 简单】 第五十三题 删除重复的电子邮箱

    编写一个 SQL 查询,来删除 Person 表中所有重复的电子邮箱,重复的邮箱里只保留 Id 最小 的那个. +----+------------------+ | Id | Email | +-- ...

  3. 关于Re模块的一些基础知识(另附一段批量抓代理ip的代码)

    1.常用匹配规则 . 表示任意字符[0-9] 用来匹配一个指定的字符类别[^5]表示除了5之外的其他字符,^不在字符串的开头,则表示它本身.* 对于前一个字符重复0到无穷次+ 对于前一个字符重复1到无 ...

  4. 21、利用selenium进行Web测试

    一.案例实施步骤思路分析 1.寻包 2.指定浏览器(实例化浏览器对象) 3.打开项目 4.找到元素(定位元素) 5.操作元素 6.暂停 7.关闭二.元素定位[重点] 1.id 说明:通过元素的id属性 ...

  5. python算法之近似熵、互近似熵算法

    理论基础 近似熵? 定义:近似熵是一个随机复杂度,反应序列相邻的m个点所连成折线段的模式的互相近似的概率与由m+1个点所连成的折线段的模式相互近似的概率之差. 作用:用来描述复杂系统的不规则性,越是不 ...

  6. spring源码解析--事务篇(前篇)

    对于每一个JAVA程序员,spring应该是再熟悉不过的框架了,它的功能有多强大我就不多说了,既然他有这么强大的功能,是如何实现的呢?这个就需要从他的原理去了解,而最直接了解原理的方式莫过于源码.当然 ...

  7. python实现IMAP协议下email收取

    本文为转载,原文在这里. 所谓无痕取信,目前主要是指从邮箱中把信件收取后,邮箱内状态不发生任何改变.这里的状态主要是指两部分,一部分是邮件状态不变,即已读与未读状态不变,另一部分是指邮箱记录的登陆IP ...

  8. 小程序canvasu真机上数据图片不能使用

    canvas遇到的坑 1.文字换行 2.真机不能使用网络数据图片(真坑) 点击显示效果我就不写了,你们可以自己加一下 全部代码贴出来 css #preview { width: 100%; heigh ...

  9. GUC-2 原子性

    import java.util.concurrent.atomic.AtomicInteger; /* * 一.i++ 的原子性问题:i++ 的操作实际上分为三个步骤“读-改-写” * int i ...

  10. Java学习(基本语句,语法,变量)

    一.基本语法: public class Demo { //定义一个类 public static void main(String[] args) { //主方法,一切程序的起点 /* 在屏幕上打印 ...