首先如果没有限制的话,我们可以直接求出答案,假设对于n*m的矩阵,我们最上方一行和左方的一列随意确定,那么首先这写确定的状态肯定是不会不合法的,因为我们可以调整剩下的01状态来使得这一行一列的状态合法,而且剩下的01状态唯一确定,我们叫最上面的行和左面的列为标准行列,那么我们对于每一种不同的标准行列都可以有一组合法解,那么最后答案的数量就是标准行列的数量,那么标准行列一共有2^(n+m-1)种方案,那么这就是最后的答案。

  那么每一个四方阵的1个数为奇数代表这个四方阵的xor和为1,那么根据xor的结合律,我们可以得到一个式子,假设a[i][j]为合法方案中[i][j]位置的数,那么a[1][1]^a[i][j]^a[1][j]^a[i][1]=0或1,当且仅当i,j都为偶数的时候等于0,那么有了这个性质我们可以根据给出的限制判断[1][j]和[i][1]位置的01选择是否相同,那么我们可以将标准行列的每一个点看成两个点,即这个位置选的是0还是1,那么根据给出的限制我们可以知道在这个点选0或1时其余某些点必须选0或1,那么这就是一个简单的2-sat模型了,我们可以连出图之后判断各个连通块中是否有不同的颜色(即题目直接对标准行列做限制),有的话即无解,那么我们可以确定出独立点的个数,即这些点的颜色确定不会对其他点造成影响。

  对于左上角选1的情况我们只需要把限制中的01状态全部取xor然后再按照上述方法做一遍就可以了。

  那么在实际操作的时候我们可以用并查集来维护及节点之间的信息,设father[x]为x的父亲节点,ww[x]为x与其父亲节点的关系,ww[x]为0时代表颜色相同,1时则不同,那么我们只需要维护这个就可以了。

  备注:在合并a,b的时候,设fa为a的父亲应该ww[fa]=ww[a]+ww[b]+a,b节点之间的关系(为01),结果开始的时候忘了ww[a]了,改了之后又把ww[b]删了,查了半天= =。

/**************************************************************
Problem: 2303
User: BLADEVIL
Language: C++
Result: Accepted
Time:1340 ms
Memory:71120 kb
****************************************************************/ //By BLADEVIL
#include <cstdio>
#include <cstring>
#define maxn 3000010
#define d39 1000000000
#define LL long long using namespace std; struct rec {
int x,y,z;
}ask[maxn]; int n,m,N,k,w,ans;
int flag[maxn],father[maxn],ww[maxn]; int getfather(int x) {
if (father[x]==x) return x;
int fa=father[x];
father[x]=getfather(fa);
ww[x]=(ww[x]+ww[fa])%;
return father[x];
} int pwr(int x,int k) {
int ans=;
while (k) {
if (k&) ans=(LL)ans*x%d39;
k>>=;
x=(LL)x*x%d39;
}
return ans;
} int calc() {
memset(flag,,sizeof flag);
memset(ww,,sizeof ww);
N=n+m-;
for (int i=;i<=N;i++) father[i]=i;
for (int i=;i<=k;i++) {
if (ask[i].x==) {
int cur;
if (ask[i].z) cur=; else cur=;
if (flag[ask[i].y-]) {
if (flag[ask[i].y-]!=cur) return ; else flag[ask[i].y-]=cur;
} else flag[ask[i].y-]=cur;
continue;
}
if (ask[i].y==) {
int cur;
if (ask[i].z) cur=; else cur=;
if (flag[ask[i].x+m-]) {
if (flag[ask[i].x+m-]!=cur) return ; else flag[ask[i].x+m-]=cur;
} else flag[ask[i].x+m-]=cur;
continue;
}
int a=ask[i].y-,b=ask[i].x+m-;
//printf("%d %d\n",a,b);
int fa=getfather(a),fb=getfather(b),add=;
if ((ask[i].x%==)&&(ask[i].y%==)) {
if (ask[i].z) add=; else add=;
} else {
if (ask[i].z) add=; else add=;
}
//printf("%d\n",add);
if (fa==fb) {
int cur=(ww[a]+ww[b])%;
if (cur!=add) return ;
} else father[fa]=fb; ww[fa]=(ww[b]+ww[a]+add)%;
//for (int i=1;i<=N;i++) printf("|%d %d %d %d\n",i,father[i],ww[i],flag[i]);
}
//for (int i=1;i<=N;i++) printf("|%d %d %d\n",i,father[i],ww[i]);
//for (int i=1;i<=N;i++) printf("%d ",flag[i]); printf("\n");
int cnt=;
for (int i=;i<=N;i++) if (father[i]==i) cnt++;
//printf("%d ",cnt);
for (int i=;i<=N;i++) if (flag[i]) {
if (!flag[getfather(i)]) flag[getfather(i)]=(flag[i]+ww[i])%+; else
if (flag[getfather(i)]!=(flag[i]+ww[i])%+) return ;
}
//for (int i=1;i<=N;i++) printf("%d ",flag[i]); printf("\n");
for (int i=;i<=N;i++) if ((father[i]==i)&&(flag[i])) cnt--;
//printf("%d\n",cnt);
return pwr(,cnt);
} int main() {
scanf("%d%d%d",&n,&m,&k);
for (int i=;i<=k;i++) scanf("%d%d%d",&ask[i].x,&ask[i].y,&ask[i].z);
for (int i=;i<=k;i++) if ((ask[i].x==)&&(ask[i].y==)) w=i;
//calc(); return 0;
if (w) {
if (ask[w].z)
for (int i=;i<=k;i++) ask[i].z^=;
ans=calc();
} else {
ans=calc()%d39;
for (int i=;i<=k;i++) ask[i].z^=;
(ans+=calc())%=d39;
}
printf("%d\n",ans);
return ;
}

bzoj 2303 并查集的更多相关文章

  1. bzoj 1015 并查集

    逆向思维,先将整张图以最后所有要求的点毁掉的状态建图,然后倒着 加点就行了,用并查集维护连通块 /*************************************************** ...

  2. bzoj 1171 并查集优化顺序枚举 | 线段树套单调队列

    详见vfleaking在discuss里的题解. 收获: 当我们要顺序枚举一个序列,并且跳过某些元素,那么我们可以用并查集将要跳过的元素合并到一起,这样当一长串元素需要跳过时,可以O(1)跳过. 暴力 ...

  3. bzoj 1854 并查集 + 贪心

    思路:这个题的并查集用的好NB啊, 我们把伤害看成图上的点,武器作为边,对于一个联通块来说, 如果是一棵大小为k的树,那么这个联通块里面有k - 1个伤害能被取到,如果图上有环那么k个值都能 取到,对 ...

  4. bzoj 1202 并查集

    首先我们知道若干区间和信息,判断给出信息是否合法,可以用并查集维护,我们用dis[x]表示x到father[x]的距离为多少,即区间father[x]到x的长度,这样我们可以在路径压缩的时候维护dis ...

  5. BZOJ 3910 并查集+线段树合并

    思路: 1. 并查集+线段树合并 记得f[LCA]==LCA的时候 f[LCA]=fa[LCA] 2.LCT(并不会写啊...) //By SiriusRen #include <cstdio& ...

  6. BZOJ 1116 并查集

    思路: 如果 每个联通块 边数>=点数 就OK 用并查集搞 //By SiriusRen #include <cstdio> #include <cstring> #in ...

  7. BZOJ 1015 并查集+离线倒序

    统计块个数写错了调了好久啊,BZOJ1696的弱化版本. #include <iostream> #include <cstring> #include <algorit ...

  8. bzoj 1050 并查集

    先按边长排序,假设s与t连通,那么我们可以枚举s与t的路径中最短的一条边,通过类似与kruskal的方法找到s与t的路径在当前最小边权情况下尽量小的最大边权,用这个比值更新答案. 特别的,我们对于某一 ...

  9. BZOJ 3624 并查集 (Kruskal)

    思路: 先把所有能加上的水泥路都加上 判断哪些是必加的鹅卵石路 再重新做一遍最小生成树 加上必加的鹅卵石路 一直加鹅卵石路 判一下是不是=k 最后加上水泥路就好了 //By SiriusRen #in ...

随机推荐

  1. TeamCity编译执行selenium上传窗口脚本缺陷

    2015-07-04 18:05 编写本文 TeamCity编译selenium脚本,对于上传窗口处理只支持sendKeys的使用,不支持模拟人为按下Enter键和使用autoIt等操作,即使本地调试 ...

  2. 使用java程序模拟页面发送http的post请求

    在web应用程序中,一般都是通过页面发送http的post请求,但也可以使用java程序来模拟页面发送请求,代码如下: import java.io.BufferedReader; import ja ...

  3. PHP中继承

    继承 基本概念 将一个类A中的特性信息,传递到另一个类B中,此时就称为: B继承A A派生出B: 基本语法: 几个基本概念 继承:一个类从另一个已有的类获得其特性,称为继承. 派生:从一个已有的类产生 ...

  4. 第208天:jQuery框架封装(一)

    一.事件框架 1.DOM2 --事件流 事件流 :冒泡 捕获 1.1冒泡:事件按照从最特定的事件目标到最不特定的事件目标(document对象或者body)的顺序触发. 1.1.1浏览器兼容问题处理 ...

  5. Arrays.toString 如果传入的是对象 那么调用的是此对象的toString

    Arrays.toString(Object[] obj) 如果传入参数的是对象 那么调用的是此对象的toString

  6. 深入理解JVM一性能监控工具

    一.前言 工欲善其事必先利其器,性能优化和故障排查在我们大都数人眼里是件比较棘手的事情,一是需要具备一定的原理知识作为基础,二是需要掌握排查问题和解决问题的流程.方法.本文就将介绍利用性能监控工具,帮 ...

  7. jsp - redirect重定向 / forward转发

    redirect:请求重定向: 客户端行为,本质上为2次请求,地址栏改变,前一次请求对象不保存, 所以请求携带的数据会丢失. 举例:你去银行办事(forward.jsp),结果告诉你少带了东西,你得先 ...

  8. Codeforces Round #431

    我太菜啦 A 一道斯波题,我想了一会儿后写了dp,其实if就好了 B做法很一眼,但有一些细节,分类一下就好了 C一直在想dp,挂机30分钟,后来dp来模拟分层图状态扩展的过程 D不会 然后发现room ...

  9. 被动式pocscan扫描神器搭建

    1.搭建环境: 操作系统为:ubuntu16.04 x64位系统,内核版本3.0.10以上 2.安装docker镜像 root@backlion-virtual-machine:/# apt-get ...

  10. js new date()说明

    javaScript UTC() 方法: UTC() 方法可根据世界时返回 1970 年 1 月 1 日 到指定日期的毫秒数. 要创建一个一个日期对象,可以使用以下的方式: var now=new D ...