bzoj3495 PA2010 Riddle(2-SAT 前缀优化建边)

来源:互联网 发布:淘宝网跑步机 编辑:程序博客网 时间:2024/05/29 02:18

bzoj3495 PA2010 Riddle

原题地址:http://www.lydsy.com/JudgeOnline/problem.php?id=3495

题意:
k个国家,几个城市,m条边。
要求每个国家有且仅有一个首都,每条边两端的城市至少要有一个首都。
判断是否有解, 有解输出“TAK”,无解输出”NIE”。

数据范围
1 < = k, N ,M , < =1000000

题解:
传统建边O(n^2),舍弃,考虑前缀优化。
解法是前缀一个点拆成四个点,分别表示:这个城市是首都/这个城市不是首都、这个城市所属国家中到这个城市的前缀包含首都\不包含首都。
令城市点为i,对应前缀点pre[i],上面加个 ` 表示反点。这里的建边都表示同时建了反变(u>vv>u
那么
i>j
i>pre[i]
i>pre[i1]
pre[i]>pre[i1]
跑2-SAT即可。


这道题让我想到了UOJ 210
很容易想到前后缀建边优化,可以从这类题中归纳出一个建边方向的问题 。
UOJ210的前后缀可以直接与单独的点相关,因为前缀/后缀假话点可以直接指向这个人是犯人,而这个人是犯人却不能直接指向某条供词是假的。
此题,前后缀城市有首都 也不能对应到某个城市是不是首都 ,因此是点的真连向前缀的真。

但是网上很多题解说只需要前缀即可,为什么?
实际上,只建前缀点并不能保证只有一个首都,可能合法方案是一个国家根本没有首都。所有的前缀都是假的。

那么建立后缀节点呢?前缀全假总能对应后缀为真了吧。

即使建立了后缀点,那么我们确实杜绝了一个国家的所有前后缀都不是首都的情况,
但这个前后缀是首都, 并不能指向某个城市是首都。
只有前后缀不是首都,才能指向某个城市不是首都。
还是有可能一个国家全部不是首都。
所以建不建后缀的效果相同。保证的都是<=1却没保证。(因此UOJ210也可以只前缀优化)
那么为什么这样可行呢?
假设我们构造出一组”合法”方案,但其中存在某个国家没有首都,所有前缀都为假。
观察原建边方式可知,由于”两端的城市至少要有一个首都” ,可以都是首都
如果我们定义是首都/前缀包含首都为真,不是首都/前缀不包含首都为假:
发现城市的真只连向对应前缀的真,和对应前缀前一个前缀的假,
而对应前缀的真只会连向后一个城市的假,和后一个前缀的真,
以此类推,发现如果任意把一个城市改为真,影响的只会是之后的前缀变为了真,而这些前缀并不能指向某个城市的真。
(剩下的本来就是假的现在还是假的)
于是对于没有首都的国家,任意改变一个城市为首都是可行的。
于是对于这种建图,如果判定有解,总能找出一组合法解,究其原因就是因为“每条边两端的城市至少要有一个首都”这个条件。


代码:

#include<cstdio>#include<cstring>#include<algorithm>#include<iostream>using namespace std;const int N=1000100;int n,m,k,id[N],idc=1,pl[4*N],cnt=0,stack[4*N],top=0,inc=0,dfn[4*N],low[4*N];int head[4*N],nxt[16*N],to[16*N],num=1;bool ins[4*N];void build(int u,int v){    num++;    to[num]=v;    nxt[num]=head[u];    head[u]=num;}void add(int u,int v){    build(u,v); build(v^1,u^1);}void dfs(int u){    inc++; dfn[u]=low[u]=inc;    stack[++top]=u; ins[u]=1;    for(int i=head[u];i;i=nxt[i])    {        int v=to[i];        if(!dfn[v]) {dfs(v); low[u]=min(low[u],low[v]);}        else if(ins[v])        low[u]=min(low[u],dfn[v]);    }    if(low[u]==dfn[u])    {        cnt++;        while(1)        {            pl[stack[top]]=cnt;            ins[stack[top]]=0;            top--;            if(stack[top+1]==u) break;        }    }}int main(){    scanf("%d%d%d",&n,&m,&k);    for(int i=1;i<=n;i++){idc+=2; id[i]=idc;}    for(int i=1;i<=m;i++)    {        int u,v;        scanf("%d%d",&u,&v);        add(id[u]^1,id[v]);    }    for(int j=1;j<=k;j++)    {        int cn; scanf("%d",&cn);        for(int i=1;i<=cn;i++)        {            int x; scanf("%d",&x);             int pre=idc; idc+=2;            add(id[x],idc);            if(i!=1)             {                add(id[x],pre^1); //pre->id[x]                add(pre,idc);            }        }    }    for(int i=2;i<=idc;i++) if(!dfn[i]) dfs(i);    bool flag=0;    for(int i=1;i<=n;i++) if(pl[id[i]]==pl[id[i]^1]){flag=1; break;}    if(flag) printf("NIE\n");    else printf("TAK\n");    return 0;}
阅读全文
0 0
原创粉丝点击