UOJ #210. 【UER #6】寻找罪犯 2-sat 前缀优化建边 详解

来源:互联网 发布:小提琴制作材料淘宝 编辑:程序博客网 时间:2024/05/21 10:51



先%下Flaze_ 太强了Orz

这是一个2-sat

2-sat 边 u->v 的含义在于:若u则一定v

要诀就在于一定要对每一个这样的约束条件考虑完全


先来一个40分做法

裸的2-sat ~

常识两排点 表示i是or不是犯人

我们考虑:

若u不是罪犯:

则u说的都是真话,谈到的所有人身份都将确定

则所有说u是罪犯都是罪犯

若u是罪犯:

则u可能有一句话是假的,那么枚举这句假话,剩下的就都是真的

这样建图 跑2-sat就可以得到答案了

至于怎么求方案

就看其他的blog吧。


#include<cmath>#include<ctime>#include<cstdio>#include<cstring>#include<cstdlib>#include<iostream>#include<algorithm>#include<iomanip>#include<vector>#include<string>#include<bitset>#include<queue>#include<set>#include<map>using namespace std;typedef double db;typedef long long ll;inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch<='9'&&ch>='0'){x=x*10+ch-'0';ch=getchar();}return x*f;}void print(int x){if(x<0)putchar('-'),x=-x;if(x>=10)print(x/10);putchar(x%10+'0');}const int N=200100;struct EDGE{int to,nt,val;};int n,m;namespace sccc{EDGE e[30000000];int last[N],ecnt;inline void add(int u,int v){e[++ecnt]=(EDGE){v,last[u]};last[u]=ecnt;}int dfn[N],low[N],tim;int bel[N],size[N],scc;bool ins[N];int st[N],top;void tarjan(int u){dfn[u]=low[u]=++tim;st[++top]=u;ins[u]=1;for(int i=last[u],v;i;i=e[i].nt){v=e[i].to;if(!dfn[v])tarjan(v),low[u]=min(low[u],low[v]);else if(ins[v]&&dfn[v]<low[u])low[u]=dfn[v];}if(dfn[u]==low[u]){int tmp;scc++;do{tmp=st[top--];ins[tmp]=0;bel[tmp]=scc;size[scc]++;}while(tmp!=u);}}}EDGE e[N];int last[N],ecnt;inline void add(int u,int v,int val){e[++ecnt]=(EDGE){v,last[u],val};last[u]=ecnt;}int main(){n=read();m=read();register int i,j,u,v,val;for(i=1;i<=m;++i){u=read();v=read();val=read();add(u,v,val^1);}// 1-n real n+1-2n fakefor(u=1;u<=n;++u){for(i=last[u];i;i=e[i].nt){sccc::add(u,e[i].to+(e[i].val)*n);//if u is real v's identity will get downsccc::add(e[i].to+(1^e[i].val)*n,u+n);//if v cannot match u u must be fake}for(i=last[u];i;i=e[i].nt)for(j=e[i].nt;j;j=e[j].nt){sccc::add(e[i].to+(1^e[i].val)*n,e[j].to+e[j].val*n);//if e[i].to cannot match u e[j].to will surely match u sccc::add(e[j].to+(1^e[j].val)*n,e[i].to+e[i].val*n);//ditto}}for(i=1;i<=n<<1;++i)if(!sccc::dfn[i])sccc::tarjan(i);int num=0;for(i=1;i<=n;++i)if(sccc::bel[i]==sccc::bel[i+n]){puts("Impossible");return 0;}else if(sccc::bel[i]>sccc::bel[i+n]) num++;cout<<num<<endl;for(i=1;i<=n;++i)if(sccc::bel[i]>sccc::bel[i+n])print(i),putchar(' ');putchar('\n');return 0;}

之后是全分做法

之所以炸掉是因为边数太多

使用 前缀建边优化

我们加两排点到原来的后边

表示说这句话的人在这句话之前(包括这句话)说的话是不是都是真话

我们记这个东东为stc

所以每一条供词加入时的情况就变成了:

若这个人不是罪犯:

则之前的stc都为真,所指向的人身份正确

若这个人是罪犯(这句话为假):

则这个人之前的stc都为真,自己及之后的都为假,这个人一定是犯人,所指向的人的身份错误

若stc为真:

则指向的人身份确定,这个人之前的stc都为真

若之前stc为假:

则stc为假,这句一定为真,所指向的人身份正确

这样就可以完成所有的约束


#include<cmath>#include<ctime>#include<cstdio>#include<cstring>#include<cstdlib>#include<iostream>#include<algorithm>#include<iomanip>#include<vector>#include<string>#include<bitset>#include<queue>#include<set>#include<map>using namespace std;typedef double db;typedef long long ll;inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch<='9'&&ch>='0'){x=x*10+ch-'0';ch=getchar();}return x*f;}void print(int x){if(x<0)putchar('-'),x=-x;if(x>=10)print(x/10);putchar(x%10+'0');}const int N=400100;int last[N<<1],ecnt;struct EDGE{int to,nt;}e[N<<3];inline void add(int u,int v){e[++ecnt]=(EDGE){v,last[u]};last[u]=ecnt;}int n,m;int dfn[N],low[N],tim;int bel[N],size[N],scc;bool ins[N];int st[N],top;void tarjan(int u){dfn[u]=low[u]=++tim;st[++top]=u;ins[u]=1;for(int i=last[u],v;i;i=e[i].nt){v=e[i].to;if(!dfn[v])tarjan(v),low[u]=min(low[u],low[v]);else if(ins[v]&&dfn[v]<low[u])low[u]=dfn[v];}if(dfn[u]==low[u]){int tmp;scc++;do{tmp=st[top--];ins[tmp]=0;bel[tmp]=scc;size[scc]++;}while(tmp!=u);}}// criminal 0->not 1->yesinline int crm(int x,int p){return x+p*n;}// sentence 0->real 1->fakeinline int stc(int x,int p){return (n<<1)+x+p*m;}int pre[N];int main(){n=read();m=read();register int i,u,v,val;for(i=1;i<=n;++i) pre[i]=2*m+1;for(i=1;i<=m;++i){u=read();v=read();val=read()^1;add( crm(v,val^1),stc(pre[u],0) );//if this sentence is fake those before must be realadd( stc(pre[u],1),crm(v,val) );//opposite to beforeadd( stc(i,0),crm(v,val) );//if the sentence and those before are real the man he refer to must match his sentenceadd( crm(v,val^1),stc(i,1) );//opposite to beforeadd( stc(i,0),stc(pre[u],0) );//if the sentence and those before are real those before must be realadd( stc(pre[u],1),stc(i,1) );//opposite to beforepre[u]=i;}for(i=1;i<=n;++i)add( stc(pre[i],1),crm(i,1) ),add( crm(i,0),stc(pre[i],0) );for(i=1;i<=(m+n)<<1;++i)if(!dfn[i])tarjan(i);int num=0;for(i=1;i<=n;++i)if(bel[i]==bel[i+n]){puts("Impossible");return 0;}else if(bel[i]>bel[i+n]) num++;cout<<num<<endl;for(i=1;i<=n;++i)if(bel[i]>bel[i+n])print(i),putchar(' ');putchar('\n');return 0;}


阅读全文
'); })();
1 0
原创粉丝点击
热门IT博客
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 九元店 如何开十元店 投资10元店 10元饰品店 十元店的进货渠道 小商品十元店 怎样开个十元店 十元休闲店 怎样开十元店 十元店饰品批发 十元店进货价格 精品十元店 十元店从哪进货 十元精品店怎么样 十元店批发价格 十元店成本 满库十元店 全场十元店失败经验 义乌十元店批发货源 1至2元小商品批发网 临沂十元店货源批发 10元包包批发地摊货 十元店进货渠道和价格表 十元店货源批发日用百货 鸦鸿桥十元店批发市场价格表 开个10元店需要多少钱 10元精品店利润 想开个十元店进货渠道 美发产品批发 复印纸批发 吧椅批发 广州尾货批发市场 福田饰品批发进货网 日用品厂家批发 品牌尾货批发货源在哪里 美术用品批发 批发日用百货 新疆红枣批发 男装批发网 体育用品批发 手机配件批发