原题:Spoj-SOPARADE
题目大意
一个有
n
个元素的序列
给定
m
个约束条件,每个条件
询问是否有合法的序列满足条件。
Data Constraint
题解
一个结论:奇数位置只填1/2,偶数只填3/4(或者反过来),因为要求相邻两数之差的绝对值大于等于2。
可以注意到,每个约束条件里最多两个奇数和两个偶数。然后就可以将约束条件转换成2-SAT的形式了。
2-SAT
现由一些元素可以取真或假,给出若干个约束条件,要求出每个元素的取值满足所有条件。
将问题转化成图模型,先把每个点
i
拆成两个
假如现在有一个条件,i为真或者j为真,那么我们就这样连两条单向边:
- n+i -> j
n+j -> i
如果
将所有约束条件都转化成有向边之后我们就可以在图上染色来求出每个元素的取值。这里讲一种蓝书上的方法:
每次找到一个没有标记过的元素
SRC
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std ;
#define N 100000 + 10
#define M 1000000 + 10
int Node[2*M] , Next[2*M] , Head[2*N] , tot ;
bool flag , Mark[2*N] ;
int a[N] , b[N] , S[N] ;
int T , n , m , top ;
int Read() {
int num = 0 ;
char c = getchar() ;
while ( c < '0' || c > '9' ) c = getchar() ;
while ( c >= '0' && c <= '9' ) num = num * 10 + c - '0' , c = getchar() ;
return num ;
}
void link( int u , int v ) {
Node[++tot] = v ;
Next[tot] = Head[u] ;
Head[u] = tot ;
}
void MakeGraph() {
if ( a[0] == 2 ) {
link( a[1] , n + a[2] ) ;
link( n + a[2] , a[1] ) ;
link( n + a[1] , a[2] ) ;
link( a[2] , n + a[1] ) ;
}
if ( b[0] == 2 ) {
link( b[1] , n + b[2] ) ;
link( n + b[2] , b[1] ) ;
link( n + b[1] , b[2] ) ;
link( b[2] , n + b[1] ) ;
}
}
int opp( int x ) {
return x <= n ? x + n : x - n ;
}
bool DFS( int x ) {
if ( Mark[opp(x)] ) return 0 ;
if ( Mark[x] ) return 1 ;
S[++top] = x ;
Mark[x] = 1 ;
for (int p = Head[x] ; p ; p = Next[p] ) {
if ( !DFS(Node[p]) ) return 0 ;
}
return 1 ;
}
int main() {
freopen( "war.in" , "r" , stdin ) ;
freopen( "war.out" , "w" , stdout ) ;
scanf( "%d" , &T ) ;
while ( T -- ) {
tot = 0 ;
flag = 1 ;
memset( Head , 0 , sizeof(Head) ) ;
memset( Mark , 0 , sizeof(Mark) ) ;
n = Read() , m = Read() ;
for (int i = 1 ; i <= n ; i += 2 ) {
int node = i + 1 ;
if ( node <= n ) {
link( node , i ) ;
link( n + i , n + node ) ;
}
node = i - 1 ;
if ( node >= 1 ) {
link( node , i ) ;
link( n + i , n + node ) ;
}
}
for (int i = 1 ; i <= m ; i ++ ) {
int k = Read() ;
a[0] = b[0] = 0 ;
for (int i = 1 ; i <= k ; i ++ ) {
int x = Read() ;
if ( x % 2 ) b[++b[0]] = x ;
else a[++a[0]] = x ;
}
if ( a[0] > 2 || b[0] > 2 ) flag = 0 ;
MakeGraph() ;
}
for (int i = 1 ; i <= n && flag ; i ++ ) {
if ( Mark[i] || Mark[i+n] ) continue ;
top = 0 ;
if ( !DFS(i) ) {
while ( top ) Mark[S[top]] = 0 , top -- ;
if ( !DFS(n+i) ) { flag = 0 ; break ; }
}
}
if ( !flag ) printf( "rejected\n" ) ;
else printf( "approved\n" ) ;
}
return 0 ;
}
以上.