UOJ Logo zgjkt的博客

博客

【poj3683】Priest John's Busiest Day

2015-06-04 14:11:15 By zgjkt

题目大意

有$n$对新人要举行仪式,每对都有两个时间段可以选择

问:输出是否可以所有新人的仪式时间不重叠

如果可以满足楼上的条件,还需输出方案

数据范围

$1\leqslant n\leqslant 1000$


题解

对于两个冲突的时间段就只能选择其中一个,$2-SAT$

$2-SAT$有对称性,我们可以$get$到一个做题顺序

1.构图1

2.求图1的极大强连通子图(tarjan)

3.把每个子图收缩成单个节点,根据原图关系构造一个有向无环图2

4.判断是否有解,无解则输出(退出)

5. 在图2的道路全部反向,构造新图3

6.对图3进行拓扑排序

7.在图3中自底向上进行选择、删除

8.输出  

对于第5步

我们知道造出图2之后,如果选择一个节点,那么会把"选择"标记一路传下去,那么如果传到两个互相矛盾的节点,这个方案就是不成立的

然而如果改成反向的道路,就是不选择一个节点,那么会把"不选择"标记一路传下去,两个彼此矛盾的节点都不选择是成立的

所以我们传递的是"不选择"标记


程序

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;

int n,edge_tot,scc_tot,dfn_tot,top;
int l[2050],r[2050],belong[2050],opp[2050];
int dfn[2050],low[2050],q[2050],flag[2050];
struct edge{int to,next;}e[2000500],e2[2000500];
int last[2050],last2[2050],d[2050];
int colour[2050];

inline int read(){
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
inline bool coin(int x,int y){
    if ((l[x]>=r[y])||(r[x]<=l[y])) return 0;
    return 1;
}
inline void init(){
    n=read();int x;
    for(int i=1;i<=n;++i){
        l[(i<<1)-1]=read();l[(i<<1)-1]=l[(i<<1)-1]*60+read();
        r[i<<1]=read();r[i<<1]=r[i<<1]*60+read();
        x=read();
        l[i<<1]=r[i<<1]-x;r[(i<<1)-1]=l[(i<<1)-1]+x;
    }
}
inline void insert(int u,int v) {e[++edge_tot].to=v;e[edge_tot].next=last[u];last[u]=edge_tot;}
inline void reinsert(int u,int v) {d[v]++;e2[++edge_tot].to=v;e2[edge_tot].next=last2[u];last2[u]=edge_tot;}
inline void build(){
    for(int i=1;i<=n;++i)
        for(int j=i+1;j<=n;++j){
            if (coin((i<<1)-1,(j<<1)-1)) {insert((i<<1)-1,j<<1);insert((j<<1)-1,i<<1);}
            if (coin((i<<1)-1,j<<1)) {insert((i<<1)-1,(j<<1)-1);insert(j<<1,i<<1);}
            if (coin(i<<1,(j<<1)-1)) {insert(i<<1,j<<1);insert((j<<1)-1,(i<<1)-1);}
            if (coin(i<<1,j<<1)) {insert(i<<1,(j<<1)-1);insert(j<<1,(i<<1)-1);}
        }
}
void tarjan(int x){
    dfn[x]=low[x]=++dfn_tot;
    q[++top]=x;flag[x]=1;
    for(int i=last[x];i;i=e[i].next)
        if (!dfn[e[i].to]) {tarjan(e[i].to);low[x]=min(low[x],low[e[i].to]);}
        else if (flag[e[i].to]) low[x]=min(low[x],dfn[e[i].to]);
    if(low[x]==dfn[x]){
        int now=0;scc_tot++;
        while (now!=x){
            now=q[top--];
            flag[now]=0;
            belong[now]=scc_tot;
        }
    }
}
inline void rebuild(){
    edge_tot=0;
    for(int i=1;i<=2*n;++i)
        for(int j=last[i];j;j=e[j].next)
            if (belong[i]!=belong[e[j].to]) reinsert(belong[e[j].to],belong[i]);
    for(int i=1;i<=n;++i){
        opp[belong[(i<<1)-1]]=belong[i<<1];
        opp[belong[i<<1]]=belong[(i<<1)-1];
    }
}
void dfs(int x){
    if (colour[x]) return;
    colour[x]=-1;
    for(int i=last2[x];i;i=e2[i].next) dfs(e2[i].to);
}
inline void topsort(){
    for(int i=1;i<=scc_tot;++i) if (!d[i]) q[++top]=i;
    while (top){
        int now=q[top--];
        if (colour[now]) continue;
        colour[now]=1;dfs(opp[now]);
        for(int i=last2[now];i;i=e2[i].next){
            d[e2[i].to]--;
            if (!d[e2[i].to]) q[++top]=e2[i].to;
        }
    }
}
inline void write(){
    for(int i=1;i<=n;++i){
        if (colour[belong[(i<<1)-1]]==1) printf("%.2d:%.2d %.2d:%.2d\n",l[(i<<1)-1]/60,l[(i<<1)-1]%60,r[(i<<1)-1]/60,r[(i<<1)-1]%60);
        else printf("%.2d:%.2d %.2d:%.2d\n",l[i<<1]/60,l[i<<1]%60,r[i<<1]/60,r[i<<1]%60);
    }
}
int main(){
    init();
    build();
    for(int i=1;i<=2*n;++i) if (!dfn[i]) tarjan(i);
    for(int i=1;i<=n;++i)
        if (belong[i*2-1]==belong[i*2]) {puts("NO");return 0;}
    puts("YES");
    rebuild();
    topsort();
    write();
    return 0;
}

评论

暂无评论

发表评论

可以用@mike来提到mike这个用户,mike会被高亮显示。如果你真的想打“@”这个字符,请用“@@”。