CF 1635E Cars 二分图 + 拓扑

文章目录

传送门

题意

给你 n n n个点,需要给每个点定向,方向可以向右或者向左,定向之后点会朝选择的方向移动,要求满足 m m m个条件,两种不同的条件如下:

  1. i , j i,j i,j两个位置定向之后移动不会相遇。
  2. i , j i,j i,j两个位置定向之后一定会相遇。

如果不能满足输出 N O NO NO,否则输出 Y E S YES YES,并且给出定向之后的点的方向和位置。

思路

考虑两种情况的方向如何选择,首先他们两个位置一定选择不同的方向,让后根据是否相遇来调整他们的位置。那么根据第一个条件,我们给 i , j i,j i,j连无向边,那么有解的第一个条件就比较显然了,就是这个构成的图是二分图,让后现在他们的方向确定了,接下来需要确认他们的位置,我们分两种情况来讨论分别对应题目的两种情况:

  1. 假设 i i i位置取 L L L j j j位置取 R R R,由于他们不会相遇,那么 x i < x j x_i<x_j xi<xj
  2. 假设 i i i位置取 L L L j j j位置取 R R R,由于他们会相遇,那么 x i > x j x_i>x_j xi>xj

看到大于号,需要跟拓扑序联系起来,那么我们就按照从小到大的位置来填,连边就按照上面的小的连大的,最终判断是否存在环即可。

复杂度 O ( n + m ) O(n+m) O(n+m)

#include<bits/stdc++.h>
#define X first
#define Y second
#define L (u<<1)
#define R (u<<1|1)
#define Mid (tr[u].l+tr[u].r>>1)
#define pb push_back
using namespace std;

const int N=1000010,INF=0x3f3f3f3f,mod=1e9+7;
typedef long long LL;

int n,m;
vector<int>v[N];
bool flag;
int col[N],d[N];
int st[N];
struct Node {
    int x,y,z;
}p[N];
struct node {
    char ch;
    int pos;
}ans[N];

void dfs(int u,int c) {
    col[u]=c;
    st[u]=0;
    for(auto x:v[u]) {
        if(col[x]) {
            if(col[x]==c) flag=false;
            continue;
        }
        dfs(x,3-c);
    }
}

char get(int pos) {
    if(col[pos]==0) return 'L';
    if(col[pos]==1) return 'R';
    else return 'L';
}

void solve() {
    flag=true;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++) {
        int a,b,c; scanf("%d%d%d",&a,&b,&c);
        v[b].pb(c);
        v[c].pb(b);
        p[i]={a,b,c};
    }
    for(int i=1;i<=n;i++) if(!col[i]) dfs(i,1);
    if(!flag) {
        puts("NO");
        return;
    }
    for(int i=1;i<=n;i++) v[i].clear();
    for(int i=1;i<=m;i++) {
        int x,y,z;
        x=p[i].x,y=p[i].y,z=p[i].z;
        if(x==2) {
            if(col[y]==1) d[z]++,v[y].pb(z);
            else d[y]++,v[z].pb(y);
        } else {
            if(col[y]==2) d[z]++,v[y].pb(z);
            else d[y]++,v[z].pb(y);
        }
    }
    queue<int>q;
    int pos=0;
    for(int i=1;i<=n;i++) if(!d[i]) q.push(i);
    while(q.size()) {
        int u=q.front(); q.pop();
        ans[u]={get(u),pos++};
        for(auto x:v[u]) {
            if(--d[x]==0) {
                q.push(x);
            }
        }
    }
    for(int i=1;i<=n;i++) if(d[i]) {
        puts("NO");
        return;
    }
    puts("YES");
    for(int i=1;i<=n;i++) {
        printf("%c %d\n",ans[i].ch,ans[i].pos);
    }
}

int main() {
	int _=1;
	while(_--) {
		solve();
	}

    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值