hdu3715

hdu3715

题意

给出一个递归的伪代码,当 x[a[dep]] + x[b[dep]] != c[dep],就向下递归,给出a,b,c数组的值 问 dep 最大多少。其中 0 <= c[i] <= 2 ,0 <= x[i] <= 1。

分析

x 取值存在对立关系( 1或0 ),那么可以通过不等式进行建边,有三种情况,

  1. 当 c[i] = 2 时,有 A and B = 0,即不能全部为真
  2. 当 c[i] = 1 时,有 A xor B = 0,加起来不能等于1
  3. 当 c[i] = 0 时,有 A or B != 0

二分m,判断可行性

code

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<iostream>
using namespace std;
const int MAXN = 5e2 + 10;
const int MAXM = 1e4 + 10;
int n, m;
int vis[MAXN];
int flag[MAXN]; // 所属强连通分量的拓扑序
vector<int> G[MAXN], rG[MAXN]; // 注意初始化
vector<int> vs; // 后序遍历顺序的顶点列表
void addedge(int x, int y)
{
    G[x].push_back(y);  // 正向图
    rG[y].push_back(x); // 反向图
}
void dfs(int u)
{
    vis[u] = 1;
    for(int i = 0; i < G[u].size(); i++)
    {
        int v = G[u][i];
        if(!vis[v]) dfs(v);
    }
    vs.push_back(u);
}
void rdfs(int u, int k)
{
    vis[u] = 1;
    flag[u] = k;
    for(int i = 0; i < rG[u].size(); i++)
    {
        int v = rG[u][i];
        if(!vis[v]) rdfs(v, k);
    }
}
int scc() // 强连通分量的个数
{
    vs.clear();
    memset(vis, 0, sizeof vis);
    for(int i = 0; i < n; i++)
        if(!vis[i]) dfs(i);
    memset(vis, 0, sizeof vis);
    int k = 0;
    for(int i = vs.size() - 1; i >= 0; i--)
        if(!vis[vs[i]]) rdfs(vs[i], k++);
    return k;
}
bool judge()
{
    int N = n;
    n = 2 * n;
    scc();
    n /= 2;
    for(int i = 0; i < N; i++)
        if(flag[i] == flag[N + i])
            return false;
    return true;
}
int a[MAXM], b[MAXM], c[MAXM];
void init(int can)
{
    for(int i = 0; i < 2 * n; i++) G[i].clear(), rG[i].clear();
    for(int i = 0; i <= can; i++) // 注意这里是等号
    {
        if(c[i] == 2) // A and B = 0
        {
            addedge(a[i], b[i] + n);
            addedge(b[i], a[i] + n);
        }
        else if(c[i] == 1) // A xor B = 0
        {
            addedge(a[i], b[i]);
            addedge(a[i] + n, b[i] + n);
            addedge(b[i], a[i]);
            addedge(b[i] + n, a[i] + n);
        }
        else // A or B != 0
        {
            addedge(a[i] + n, b[i]);
            addedge(b[i] + n, a[i]);
        }
    }
}
void solve()
{
    int l = 0, r = m, mid;
    while(l + 1 < r)
    {
        mid = (l + r) / 2;
        init(mid);
        if(judge()) l = mid;
        else r = mid;
    }
    printf("%d\n", l + 1); // 注意上面是等号,实际数量要加1
}
int main()
{
    int T;
    scanf("%d", &T);
    while(T--)
    {
        scanf("%d%d", &n, &m);
        for(int i = 0; i < m; i++) scanf("%d%d%d", &a[i], &b[i], &c[i]);
        solve();
    }
    return 0;
}
posted @ 2017-05-01 10:16  ftae  阅读(213)  评论(0编辑  收藏  举报