题目大意
有n个位置,每个位置有一个标识,范围为1到4,限制相邻两个位置的差的绝对值至少为2。同时还给出了m个限制,每个限制选定k个位置,要求这k个位置上的标识互不相同。请你输出是否存在合法的方案使得所有位置上的标识都满足限制。 n,m<=100000
2-SAT
奇数位置和偶数位置上的标识肯定是不同的,因为相邻两个位置的差的绝对值至少为2。 当然,不止这点性质。 位置1上的标识如果为1或2,位置2的标识只能为3或4,位置3的标识只能为1或2,位置4的标识又只能为3或4。。。 事实上,如果某个奇数位置上的标识为1或2,则所有奇数位置上的标识只能为1或2,所有偶数位置的标识只能为3或4。反过来,如果某个奇数位置上的标识为3或4,则所有奇数位置上的标识只能为3或4,所有偶数位置的标识只能为1或2。由于只考虑是否存在合法方案,所以这两种情况在我们眼里是等价的,考虑其中一种即可。 那么对于一个位置,我们拆成两个点,一个表示取真(对于奇数位置表示标识取1,对于偶数位置表示标识取4),一个表示取假(对于奇数位置表示标识取3,对于偶数位置表示标识取2) 那么这就是个经典的2-SAT的问题了。 对于限制相邻两个位置的差的绝对值至少为2,相当于限制相邻两个位置至少一个取真。 那么可以通过连边(有向边)来表示限制。 例如两个位置i和j,至少一个为真,设i表示i取真的点,i+n表示i取假的点,则这样连边 i+n—>j j+n—>i 这样i取假时,j必为真;j取假时,i必为真。 另m个限制,我们可以类似的连边,有一个trick,k的取值最大为4(显然。。。) 同时奇偶位置可以分开连边,因为奇数位置和偶数位置上的标识肯定是不同的。 然后2-SAT的问题有多种算法,比如tarjan缩环之类的(身为蒟蒻并不会) 还有一种朴素的O(nm)的做法,对于所有未确定的点,取真dfs一遍,若不符要求,再取假dfs一遍,实际应用中复杂度一般都达不到理论复杂度(比如说这题),而且该算法有一个很重要的用处:求字典序最小的解!(虽然和这题没啥关系)
代码
using namespace std;
const
int maxn=
100000+
5;
int i,j,n,t,l,
m,num,cnt;
int f1[
5],f2[
5],k[maxn
*2],g[maxn
*10],
next[maxn
*10],
q[maxn*2],ba[maxn
*2];
bool bz[maxn
*2];
void add(
int x,
int y){
next[++num]=k[
x],k[
x]=num,g[num]=
y;
}
void ad(
int x,
int y){
add(
x,
y+n);add(
x+n,
y);add(
y+n,
x);add(
y,
x+n);
}
int re(
int x){
if (
x<n)
return x+n;
else return x-n;
}
bool dfs(
int x){
if (bz[
x]||(ba[
x]==cnt))
return 1;
q[++num]=
x;
ba[
x]=cnt;
int y=re(
x);
if (bz[
y]||(ba[
y]==cnt))
return 0;
int i=k[
x];
while (i){
if (!dfs(g[i]))
return 0;
i=
next[i];
}
return 1;
}
bool df(
int x){
num=
0;
cnt++;
if (dfs(
x)) {
int i;fo(i,
1,num) bz[
q[i]]=
1;
return 1;
}
return 0;
}
int main(){
scanf(
"%d",&t);
fo(l,
1,t){
scanf(
"%d%d",&n,&
m);num=
0;
memset(k,
0,sizeof(k));
memset(bz,
0,sizeof(bz));
memset(ba,
0,sizeof(ba));cnt=
0;
bool b=
1;
fo(i,
1,n) {
if (i<n) add(i+n,i+
1);
if (i>
1) add(i+n,i-
1);}
fo(i,
1,
m){
int w,t1=
0,t2=
0;
scanf(
"%d",&w);
fo(j,
1,w) {
int y;scanf(
"%d",&
y);
if (
y%2) f1[++t1]=
y;
else f2[++t2]=
y;
}
if ((t1>
2)||(t2>
2)) b=
0;
if (!b)
continue;
if (t1==
2) ad(f1[
1],f1[
2]);
if (t2==
2) ad(f2[
1],f2[
2]);
}
if (!b){
printf(
"rejected\n");
continue;}
fo(i,
1,n)
if (!bz[i]&&!bz[i+n]){
b=
0;
if (df(i)) b=
1;
else if (df(i+n)) b=
1;
if (!b)
break;
}
if (!b)
printf(
"rejected\n");
else printf(
"approved\n");
}
}
转载请注明原文地址: https://ju.6miu.com/read-1312454.html