题目链接
混合欧拉图经典问题吧。有N个点,M条边(既有有向边又有无向边),想知道是否可以构成欧拉图。
最开始的时候,我大致画了一下图,我发现一个必要不充分条件(因为WA了啊),如果有多个联通块或者是某个点的度的值为奇数,那么它肯定是无法构成混合欧拉图的——这当然是没错的,但是还不够充分,于是WA了一上午。
然后再想,我试着去枚举一些样例,然后制造出了这样的玩意儿:
很显然,这样的图肯定是无法构成一个欧拉图的,所以驳回了之前的想法。
在之后,会想欧拉图的基本性质:
- 联通
- 每个点的度为偶数
- 入度==出度
1、2两点,我们确实用到了,但是在混合图之中,我们需要考虑3号性质。怎样让那些无向边确定方向?这是目前我们为难的事情。如果我们给这些无向边一个初始方向呢,那么,我们其实确定了每个点的入度和出度。如果入度出度,那么其实就代表了其中的有些有向边要更换方向了。
如何更换方向?这肯定是一个蝴蝶效应的问题,绝对不能随意的改变一条边的方向。所以,我考虑到了最大流,如果最后我们做到了满流,其实就意味着它经过边的方向转变变成了一幅欧拉图。
现在考虑回来度,如果说目前出度>入度,那么其实意味着出边要改变方向,假如现在出度-入度=x,那么则有x/2条出边需要改变方向。反之,如果入度>出度,那么其实意味着入边要改变方向,假如现在入度-出度=x,那么同样要有x/2条入边需要改变方向。
这样,我们把网络流的框架搭建成:
这样,我们其实就是去保证所有需要更换出边方向的跑入所有需要更换入边方向的,使得最后的度是守恒的。
又有,我们很容易知道,需要更改的出边的数目是等于需要更改的入边的数目的。
所以,如果最后实现了满流,那么就是带边可以构成欧拉图了。
其中,我们还可以知道哪些边是需要反向的边,也就是跑完最大流之后流为0的边,因为他们被使用了,也就是说被要求反向了,所以这些边最后要求反向。
最后,就是网络流跑最大流了。期间,忘记把边权改大了(因为还有些点要和源点或者是汇点链接边),用ISAP的话,会TLE的。
#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#include <bitset>
//#include <unordered_map>
//#include <unordered_set>
#define lowbit(x) ( x&(-x) )
#define pi 3.141592653589793
#define e 2.718281828459045
#define INF 0x3f3f3f3f
//#define INF 10000007.
#define eps 1e-7
#define HalF (l + r)>>1
#define lsn rt<<1
#define rsn rt<<1|1
#define Lson lsn, l, mid
#define Rson rsn, mid+1, r
#define QL Lson, ql, qr
#define QR Rson, ql, qr
#define myself rt, l, r
#define MP(a, b) make_pair(a, b)
using namespace std;
typedef unsigned long long ull;
typedef unsigned int uit;
typedef long long ll;
const int maxN = 205, maxM = 1.2e3 + 7;
int N, M, in_du[maxN], out_du[maxN], root[maxN];
int fid(int x) { return x == root[x] ? x : root[x] = fid(root[x]); }
int head[maxN], cnt, cur[maxN];
struct Eddge
{
int nex, to, flow;
Eddge(int a=-1, int b=0, int c=0):nex(a), to(b), flow(c) {}
}edge[maxM << 1];
inline void addEddge(int u, int v, int w)
{
edge[cnt] = Eddge(head[u], v, w);
head[u] = cnt++;
}
inline void _add(int u, int v, int w) { addEddge(u, v, w); addEddge(v, u, 0); }
struct Max_Flow
{
int S, T, n;
int gap[maxN], d[maxN], que[maxN], ql, qr;
inline void init()
{
n = T;
for(int i=S; i<=T; i++)
{
gap[i] = d[i] = 0;
cur[i] = head[i];
}
++gap[d[T] = 1];
que[ql = qr = 1] = T;
while(ql <= qr)
{
int x = que[ql ++];
for(int i=head[x], v; ~i; i=edge[i].nex)
{
v = edge[i].to;
if(!d[v]) { ++gap[d[v] = d[x] + 1]; que[++qr] = v; }
}
}
}
inline int aug(int x, int FLOW)
{
if(x == T) return FLOW;
int flow = 0;
for(int &i=cur[x], v; ~i; i=edge[i].nex)
{
v = edge[i].to;
if(d[x] == d[v] + 1)
{
ll tmp = aug(v, min(FLOW, edge[i].flow));
flow += tmp; FLOW -= tmp; edge[i].flow -= tmp; edge[i ^ 1].flow += tmp;
if(!FLOW) return flow;
}
}
if(!(--gap[d[x]])) d[S] = n + 1;
++gap[++d[x]]; cur[x] = head[x];
return flow;
}
inline int max_flow()
{
init();
int ret = aug(S, INF);
while(d[S] <= n) ret += aug(S, INF);
return ret;
}
} MF;
inline void init()
{
cnt = 0; MF.S = 0; MF.T = N + 1;
for(int i=0; i<=N + 1; i++)
{
root[i] = i; head[i] = -1;
in_du[i] = out_du[i] = 0;
}
}
int main()
{
int T; scanf("%d", &T);
while(T--)
{
scanf("%d%d", &N, &M);
init();
for(int i=1, u, v, op, fu, fv; i<=M; i++)
{
scanf("%d%d%d", &u, &v, &op);
in_du[v]++; out_du[u]++;
if(!op)
{
_add(u, v, 1);
}
fu = fid(u); fv = fid(v);
if(fu ^ fv) root[fu] = fv;
}
int num = 0, sum = 0;
bool ok = true;
for(int i=1; i<=N; i++)
{
num += fid(i) == i;
if(num > 1) { ok = false; break; } //不联通
if(abs(in_du[i] - out_du[i]) & 1) { ok = false; break; } //度为奇数
if(in_du[i] ^ out_du[i])
{
if(in_du[i] > out_du[i])
{
_add(i, MF.T, (in_du[i] - out_du[i]) / 2);
sum += (in_du[i] - out_du[i]) / 2;
}
else _add(MF.S, i, (out_du[i] - in_du[i]) / 2);
}
}
if(!ok) { printf("impossible\n"); continue; }
if(sum == MF.max_flow()) printf("possible\n");
else printf("impossible\n");
}
return 0;
}