bzoj 4195 //4195:[NOI2015]程序自动分析 难点不是 离散化+并查集 //在线测评地址https://www.lydsy.com/JudgeOnline/problem.php?id=4195
更多题解,详见https://blog.csdn.net/mrcrack/article/details/90228694BZOJ刷题记录
难点不是 离散化+并查集 这两点很熟练,以下内容没有考虑到,是无法AC的。
难点的核心在于:联通具有传递性,不联通不具有传递性。2019-10-10 23:11
而是,要将关联的点先联通起来。根据这个结果,再判定接下来不关联点的联通情况:若判定还是不联通YES;若联通,矛盾NO.
//提供一组样例给读者 2019-10-10 23:09
/*
输入
1
3
1 2 0
2 3 0
1 3 1
输出
YES
*/
3556 kb | 2744 ms | C++/Edit | 1901 B |
//4195:[NOI2015]程序自动分析
//在线测评地址https://www.luogu.org/problem/P1955
//只是看完题目描述部分,感觉很像并查集
//1<=n<=10^6,1<=i,j<=10^9能明显看出要离散化
//该题离散化做好了,也就成功了90%.2019-10-8 22:26
//思路清晰,但感觉一直卡在离散化上。
//离散化时,遇到相等时,如何处理,全部扫一遍处理吗。
//离散化编写完成,突然发现,在并查集方面,逻辑出了问题,合并,判断不可满足,该如何界定呢?
//仔细想了想,
/*
分4种情况
c[i/2]==0&&f1==f2
c[i/2]==0&&f1!=f2
c[i/2]==1&&f1!=f2
c[i/2]==1&&f1==f2
去除无效的,分2种情况。
c[i/2]==0&&f1==f2
c[i/2]==1&&f1!=f2
*/
//样例通过,提交20分,测试点3-8,10WA,测试点2RE,有点失望,但心没凉。2019-10-10 17:31
//想了想,逻辑上有问题,还是要设置标记,若再次遇到同样的约束条件,需判断前后是否一致
//编写过程中,发现几处问题,如下
/*
int c[maxn],f[maxn*2],vis[maxn*2];//此处错写成int c[maxn],f[maxn],vis[maxn];猜测因此造成RE
sort(a+0,a+0+n*2,cmp1);//此处错写成sort(a+0,a+0+n,cmp1);
sort(a+0,a+0+n*2,cmp2);//此处错写成sort(a+0,a+0+n,cmp2);
*/
//深入排查的过程中,发现离散化代码有问题,以下为错误代码
/*
cnt=0,a[0].v=cnt;
for(i=1;i
else a[i].v=++cnt;
*/
//以下为修改后代码
/*
cnt=0,before=a[0].v,a[0].v=cnt;//此处错写成cnt=0,a[0].v=cnt;
for(i=1;i
else before=a[i].v,a[i].v=++cnt;//此处错写成else a[i].v=++cnt;
*/
//满怀期待,提交10分,测试点2-10WA.2019-10-10 18:15
//哪错了,感觉编到现在,很难查出问题所在。
//翻看n篇文章,才发现,此文https://www.luogu.org/problemnew/solution/P1955?page=2 作者: loi_吃瓜群众 更新时间: 2018-09-28 17:09思路写得真不错,摘抄如下
/*
首先明确一点:相等具有传递性,不相等不具有传递性(Eg:若x1等于x2,x2等于x3时,显然x1=x3。但当x1不等于x2,x2不等于x3时,x1不一定不等于x3)。
因此,所有ei相等的的约束条件之间互不冲突。
所以,只有在当ei=0的约束条件与已有的其他ei=1的约束条件发生冲突时,程序才会输出NO。
大体思路:
开一个并查集,这个并查集维护的是一个或多个相等的数的集合:
1.将形似(xi,xj,ei(=1))的约束条件看做为一个将xi,xj两个数所属的集合合并为一个集合的操作(这个操作是个简单的并查集合并);
2.将形似(xi,xj,ei(=0))的约束条件看做一个询问操作。如果fa[xi]=fa[xj],那么这些约束条件即为发生了冲突(fa[xi]=fa[xj]说明xi=xj,但当前的约束条件告诉你xi不等于xj,故冲突)。
提醒:先执行ei=1的操作,再执行ei=0的操作(因为我们是拿ei=0的操作去和所有ei=1的操作去比较看是否冲突)。
最后注意xi和xj的数据范围(1<=xi,xj<=1e9),所以离散化输入的xi,xj(不然并查集数组开不下)。
*/
//样例通过,提交AC.2019-10-10 22:28
//提供一组样例给读者 2019-10-10 23:09
/*
输入
1
3
1 2 0
2 3 0
1 3 1
输出
YES
*/
#include
#include
#include
#define maxn 100010
using namespace std;
struct node{
int v,seq;
}a[maxn*2];
int c[maxn],f[maxn*2];//此处错写成int c[maxn],f[maxn],vis[maxn];猜测因此造成RE
int cmp1(node a,node b){
return a.v
int cmp2(node a,node b){
return a.seq
int getf(int u){
return f[u]==u?u:f[u]=getf(f[u]);
}
int main(){
int i,t,n,cnt,f1,f2,flag,u,v,before;
scanf("%d",&t);
while(t--){
scanf("%d",&n);
for(i=0;i<2*n;i+=2){
scanf("%d%d%d",&a[i].v,&a[i+1].v,&c[i/2]);
a[i].seq=i,a[i+1].seq=i+1;
}
sort(a+0,a+0+n*2,cmp1);//此处错写成sort(a+0,a+0+n,cmp1);//sort 左闭右开
cnt=0,before=a[0].v,a[0].v=cnt;//此处错写成cnt=0,a[0].v=cnt;
for(i=1;i
else before=a[i].v,a[i].v=++cnt;//此处错写成else a[i].v=++cnt;
sort(a+0,a+0+n*2,cmp2);//此处错写成sort(a+0,a+0+n,cmp2);
for(i=0;i<=cnt;i++)f[i]=i;
flag=1;
for(i=0;i<2*n;i+=2){//先处理c[i]=1
if(c[i/2]==1){
u=a[i].v,v=a[i+1].v;
f1=getf(u),f2=getf(v);
if(f1!=f2)f[f2]=f[f1];//左靠
}
}
for(i=0;i<2*n;i+=2)//再处理c[i]=0
if(c[i/2]==0){
u=a[i].v,v=a[i+1].v;
f1=getf(u),f2=getf(v);
if(f1==f2){
flag=0;
break;
}
}
if(flag==1)printf("YES\n");
else printf("NO\n");
}
return 0;
}
//4195:[NOI2015]程序自动分析
//在线测评地址https://www.luogu.org/problem/P1955
//只是看完题目描述部分,感觉很像并查集
//1<=n<=10^6,1<=i,j<=10^9能明显看出要离散化
//该题离散化做好了,也就成功了90%.2019-10-8 22:26
//思路清晰,但感觉一直卡在离散化上。
//离散化时,遇到相等时,如何处理,全部扫一遍处理吗。
//离散化编写完成,突然发现,在并查集方面,逻辑出了问题,合并,判断不可满足,该如何界定呢?
//仔细想了想,
/*
分4种情况
c[i/2]==0&&f1==f2
c[i/2]==0&&f1!=f2
c[i/2]==1&&f1!=f2
c[i/2]==1&&f1==f2
去除无效的,分2种情况。
c[i/2]==0&&f1==f2
c[i/2]==1&&f1!=f2
*/
//样例通过,提交20分,测试点3-8,10WA,测试点2RE,有点失望,但心没凉。2019-10-10 17:31
//想了想,逻辑上有问题,还是要设置标记,若再次遇到同样的约束条件,需判断前后是否一致
//编写过程中,发现几处问题,如下
/*
int c[maxn],f[maxn*2],vis[maxn*2];//此处错写成int c[maxn],f[maxn],vis[maxn];猜测因此造成RE
sort(a+0,a+0+n*2,cmp1);//此处错写成sort(a+0,a+0+n,cmp1);
sort(a+0,a+0+n*2,cmp2);//此处错写成sort(a+0,a+0+n,cmp2);
*/
//深入排查的过程中,发现离散化代码有问题,以下为错误代码
/*
cnt=0,a[0].v=cnt;
for(i=1;i
else a[i].v=++cnt;
*/
//以下为修改后代码
/*
cnt=0,before=a[0].v,a[0].v=cnt;//此处错写成cnt=0,a[0].v=cnt;
for(i=1;i
else before=a[i].v,a[i].v=++cnt;//此处错写成else a[i].v=++cnt;
*/
//满怀期待,提交10分,测试点2-10WA.2019-10-10 18:15
//哪错了,感觉编到现在,很难查出问题所在。
//以下为10分代码,提供给大家。问题在于,没有先算有关联,再算无关联。而将其杂在一起算。
#include
#include
#include
#define maxn 100010
using namespace std;
struct node{
int v,seq;
}a[maxn*2];
int c[maxn],f[maxn*2],vis[maxn*2];//此处错写成int c[maxn],f[maxn],vis[maxn];猜测因此造成RE
int cmp1(node a,node b){
return a.v
int cmp2(node a,node b){
return a.seq
int getf(int u){
return f[u]==u?u:f[u]=getf(f[u]);
}
int main(){
int i,t,n,cnt,f1,f2,flag,u,v,before;
scanf("%d",&t);
while(t--){
scanf("%d",&n);
for(i=0;i<2*n;i+=2){
scanf("%d%d%d",&a[i].v,&a[i+1].v,&c[i/2]);
a[i].seq=i,a[i+1].seq=i+1;
}
sort(a+0,a+0+n*2,cmp1);//此处错写成sort(a+0,a+0+n,cmp1);//sort 左闭右开
cnt=0,before=a[0].v,a[0].v=cnt;//此处错写成cnt=0,a[0].v=cnt;
for(i=1;i
else before=a[i].v,a[i].v=++cnt;//此处错写成else a[i].v=++cnt;
sort(a+0,a+0+n*2,cmp2);//此处错写成sort(a+0,a+0+n,cmp2);
for(i=0;i<=cnt;i++)f[i]=i;
flag=1,memset(vis,0,sizeof(vis));
for(i=0;i<2*n;i+=2){
u=a[i].v,v=a[i+1].v;
f1=getf(u),f2=getf(v);
if(vis[u]==1&&vis[v]==1){
if(c[i/2]==0&&f1==f2){//互不认识&&认识 矛盾
flag=0;
break;
}else if(c[i/2]==1&&f1!=f2){//认识&&互不认识 矛盾
flag=0;
break;
}
}else{
vis[u]=1,vis[v]=1;
if(c[i/2]==1)f[f2]=f1;//左靠
}
}
if(flag==1)printf("YES\n");
else printf("NO\n");
}
return 0;
}