bzoj 4195 //4195:[NOI2015]程序自动分析 难点不是 离散化+并查集

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 kb2744 msC++/Edit1901 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<n;i++)//离散化
    if(a[i].v==a[i-1].v)a[i].v=cnt;
    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<n*2;i++)//此处错写成for(i=1;i<n;i++)//离散化
    if(a[i].v==before)a[i].v=cnt;//此处错写成if(a[i].v==a[i-1].v)a[i].v=cnt;
    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 <cstdio>
#include <algorithm>
#include <cstring>
#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<b.v;
}
int cmp2(node a,node b){
    return a.seq<b.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<n*2;i++)//此处错写成for(i=1;i<n;i++)//离散化
            if(a[i].v==before)a[i].v=cnt;//此处错写成if(a[i].v==a[i-1].v)a[i].v=cnt;
            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<n;i++)//离散化
    if(a[i].v==a[i-1].v)a[i].v=cnt;
    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<n*2;i++)//此处错写成for(i=1;i<n;i++)//离散化
    if(a[i].v==before)a[i].v=cnt;//此处错写成if(a[i].v==a[i-1].v)a[i].v=cnt;
    else before=a[i].v,a[i].v=++cnt;//此处错写成else a[i].v=++cnt;
*/
//满怀期待,提交10分,测试点2-10WA.2019-10-10 18:15
//哪错了,感觉编到现在,很难查出问题所在。
//以下为10分代码,提供给大家。问题在于,没有先算有关联,再算无关联。而将其杂在一起算。
#include <cstdio>
#include <algorithm>
#include <cstring>
#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<b.v;
}
int cmp2(node a,node b){
    return a.seq<b.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<n*2;i++)//此处错写成for(i=1;i<n;i++)//离散化
            if(a[i].v==before)a[i].v=cnt;//此处错写成if(a[i].v==a[i-1].v)a[i].v=cnt;
            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;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值