ORZ
どこでもドア
一个我当年学2-sat的博客。
2-sat
现在来口胡一下。
2-sat问题大致就是每个人有两个选项,又有一些形如某人选了选项1/2另某个人就必须/不能选选项1/2的限制,求一种满足所有限制的选择方案。
2以上的sat问题被证明是np完全的,而2-sat有O(n)的做方法。
做法
对每个选了a就必须选b的限制,a向b连边(同时就有b’向a’连边),然后先塔尖再拓排即可。
塔尖强联通缩点之后,若x和x’在同一强连通分量内显然无解,否则一定能构造出合法解。
给出一种合法的构造方式,缩点后把原图的边反向连,拓排,每次选择出队的点x并把x’和所有x’前面的点删除设为不能选。
感性口胡
1)因为如果存在边a->b一定存在边b’->a’,故图存在很强的对称性,一个强联通分量内的点的相反点也在同一个强连通分量内,故缩点后构造方案和原图方案等价。
2)缩点后的DAG上通过上诉拓排一定能构造出合法解。
1.不能选的点已经被删除了,所以拓排当前出队的点一定能选。
2.不存在x和x’同时被删除。若x在a被选的时候被删除,x’在b被选的时候被删除,设a先于b被选。x->a’(a->x’),x’->b’(b->x),即a被选时b也会被删。
题目
bzoj1823: [JSOI2010]满汉全席
どこでもドア
2-sat模板题,仅判断是否合法。
//Achen
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<vector>
#include<queue>
#include<ctime>
#include<cmath>
const int N=1007;
typedef long long LL;
using namespace std;
int T,n,m,bel[N],dfn[N],low[N];
template<typename T> void read(T &x) {
char ch=getchar(); x=0; T f=1;
while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
if(ch=='-') f=-1,ch=getchar();
for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0'; x*=f;
}
int ecnt,fir[N],nxt[20007],to[20007];
void add(int u,int v) {
nxt[++ecnt]=fir[u]; fir[u]=ecnt; to[ecnt]=v;
}
int get() {
int x; char o=getchar();
while(o!='h'&&o!='m') o=getchar(); read(x);
return o=='h'?(x<<1):(x<<1|1);
}
int que[N],dfs_clock,top,tot;
void tarjan(int x) {
dfn[x]=low[x]=++dfs_clock;
que[++top]=x;
for(int i=fir[x];i;i=nxt[i]) {
if(!dfn[to[i]]) {
tarjan(to[i]);
low[x]=min(low[x],low[to[i]]);
}
else if(!bel[to[i]]) low[x]=min(low[x],dfn[to[i]]);
}
if(dfn[x]==low[x]) {
tot++;
while(top) {
int tp=que[top--];
bel[tp]=tot;
if(tp==x) break;
}
}
}
int main() {
#ifdef DEBUG
freopen(".in","r",stdin);
freopen(".out","w",stdout);
#endif
read(T);
while(T--) {
read(n); read(m); ecnt=0;
memset(dfn,0,sizeof(dfn));
memset(bel,0,sizeof(bel));
memset(fir,0,sizeof(fir));
for(int i=1;i<=m;i++) {
int x=get();
int y=get();
add(x^1,y); add(y^1,x);
}
dfs_clock=tot=0;
for(int i=2;i<=(n<<1|1);i++) if(!dfn[i]) tarjan(i);
int fl=0;
for(int i=2;i<=(n<<1|1);i+=2)
if(bel[i]==bel[i|1]) {
fl=1; break;
}
if(fl) puts("BAD");
else puts("GOOD");
}
return 0;
}
POJ - 3683 Priest John’s Busiest Day
どこでもドア
有n对人,每对有两个时间段可以选,问所有人的时间不重叠的方案。
2-sat模板,输出方案
//Achen
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<vector>
#include<queue>
#include<ctime>
#include<cmath>
const int N=2007;
typedef long long LL;
using namespace std;
int n,st[N],ed[N];
template<typename T> void read(T &x) {
char ch=getchar(); x=0; T f=1;
while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
if(ch=='-') f=-1,ch=getchar();
for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0'; x*=f;
}
int ecnt,fir[N],nxt[N*N],to[N*N];
void add(int u,int v) {
nxt[++ecnt]=fir[u]; fir[u]=ecnt; to[ecnt]=v;
}
int cnt,fi[N],nx[N*N],tt[N*N],in[N];
void ADD(int u,int v) {
nx[++cnt]=fi[u]; fi[u]=cnt; tt[cnt]=v; in[v]++;
}
int xj(int x,int y) {
return !(ed[x]<=st[y]||st[x]>=ed[y]);
}
int tot,dfs_clock,dfn[N],low[N],que[N],top,bl[N];
void tarjan(int x) {
que[++top]=x;
dfn[x]=low[x]=++dfs_clock;
for(int i=fir[x];i;i=nxt[i]) {
if(!dfn[to[i]]) {
tarjan(to[i]);
low[x]=min(low[x],low[to[i]]);
}
else if(!bl[to[i]]) low[x]=min(low[x],dfn[to[i]]);
}
if(low[x]==dfn[x]) {
tot++;
while(top) {
int tp=que[top--];
bl[tp]=tot;
if(tp==x) break;
}
}
}
int vis[N],op[N];
void dfs(int x) {
if(vis[x]) return;
vis[x]=-1;
for(int i=fi[x];i;i=nx[i])
dfs(tt[i]);
}
void tpsort() {
top=0;
for(int i=1;i<=tot;i++)
if(!in[i]) que[++top]=i;
while(top) {
int x=que[top--];
if(vis[x]) continue;
vis[x]=1; dfs(op[x]);
for(int i=fi[x];i;i=nx[i]) {
int y=tt[i];
in[y]--;
if(!in[y]) que[++top]=y;
}
}
}
void print(int x) {
printf("%.2d:",x/60);
printf("%.2d ",x%60);
}
int main() {
#ifdef DEBUG
freopen(".in","r",stdin);
freopen(".out","w",stdout);
#endif
read(n);
for(int i=1;i<=n;i++) {
int x,y,z;
read(x); read(y);
st[i<<1]=x*60+y;
read(x); read(y);
ed[i<<1|1]=x*60+y;
read(z);
ed[i<<1]=st[i<<1]+z;
st[i<<1|1]=ed[i<<1|1]-z;
}
for(int i=2;i<=2*n+1;i++)
for(int j=i+1;j<=2*n+1;j++)
if(i!=j&&(i!=(j^1))&&xj(i,j))
add(i,j^1),add(j,i^1);
for(int i=2;i<=2*n+1;i++) if(!dfn[i]) tarjan(i);
for(int i=1;i<=n;i++) {
if(bl[i<<1]==bl[i<<1|1]) {
puts("NO"); return 0;
}
else {
op[bl[i<<1]]=bl[i<<1|1];
op[bl[i<<1|1]]=bl[i<<1];
}
}
puts("YES");
for(int i=2;i<=2*n+1;i++)
for(int j=fir[i];j;j=nxt[j])
if(bl[i]!=bl[to[j]])
ADD(bl[to[j]],bl[i]);
tpsort();
for(int i=1;i<=n;i++) {
if(vis[bl[i<<1]]==1) {
print(st[i<<1]); printf(" "); print(ed[i<<1]); puts("");
}
else {
print(st[i<<1|1]); printf(" "); print(ed[i<<1|1]); puts("");
}
}
return 0;
}
[NOI2017]游戏
どこでもドア
暴力枚举每种x当作a,b,c中的哪一种,然后2-sat判断即可.
//Achen
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<vector>
#include<cstdio>
#include<queue>
#include<cmath>
#include<set>
#define For(i,a,b) for(int i=(a);i<=(b);i++)
#define Rep(i,a,b) for(int i=(a);i>=(b);i--)
const int N=400007;
typedef long long LL;
typedef double db;
using namespace std;
int n,d,m,no[10],col[N],tid[N][4]; // A:1 B:2 C:3
char s[N],ss[10];
template<typename T> void read(T &x) {
char ch=getchar(); x=0; T f=1;
while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
if(ch=='-') f=-1,ch=getchar();
for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0'; x*=f;
}
struct node {
int a,b,x,y;
}q[N];
int ecnt,fir[N],nxt[N<<1],to[N<<1];
void add(int u,int v) {
nxt[++ecnt]=fir[u]; fir[u]=ecnt; to[ecnt]=v;
}
int dfs_clock,dfn[N],low[N],bl[N],op[N],tot,sta[N],top;
void tarjan(int x) {
dfn[x]=low[x]=++dfs_clock;
sta[++top]=x;
for(int i=fir[x];i;i=nxt[i]) {
if(!dfn[to[i]]) {
tarjan(to[i]);
low[x]=min(low[x],low[to[i]]);
}
else if(!bl[to[i]]) low[x]=min(low[x],dfn[to[i]]);
}
if(low[x]==dfn[x]) {
tot++;
for(;;) {
int y=sta[top--];
bl[y]=tot;
if(y==x) break;
}
}
}
int fi[N],nx[N],tt[N],ec,in[N];
void ADD(int u,int v) {
nx[++ec]=fi[u]; fi[u]=ec; tt[ec]=v; in[v]++;
}
queue<int>que;
int vis[N];
void dfs(int x) {
vis[x]=-1;
for(int i=fi[x];i;i=nx[i]) if(!vis[tt[i]])
dfs(tt[i]);
}
void tpsort() {
For(i,1,tot) if(!in[i]) que.push(i);
while(!que.empty()) {
int x=que.front();
que.pop();
if(vis[x]) continue;
vis[x]=1;
dfs(op[x]);
for(int i=fi[x];i;i=nx[i]) {
in[tt[i]]--;
if(!in[tt[i]]) que.push(tt[i]);
}
}
}
int main() {
#ifdef DEBUG
freopen(".in","r",stdin);
freopen(".out","w",stdout);
#endif
read(n); read(d);
scanf("%s",s+1);
read(m);
For(i,1,m) {
read(q[i].a); scanf("%s",ss); q[i].x=ss[0]-'A'+1;
read(q[i].b); scanf("%s",ss); q[i].y=ss[0]-'A'+1;
}
int up=(3<<d)-1;
For(ti,0,up) {
For(i,1,n*2) fir[i]=0,dfn[i]=0,bl[i]=0,vis[i]=0;
int tp=ti; ecnt=0; tot=0;
For(i,1,d) {
no[i]=tp%3; tp/=3;
}
int pos=0;
For(i,1,n) {
if(s[i]=='x') pos++;
if(s[i]=='a'||(s[i]=='x'&&no[pos]==0)) tid[i][1]=0,tid[i][2]=i,tid[i][3]=i+n,col[i]=2,col[i+n]=3;
if(s[i]=='b'||(s[i]=='x'&&no[pos]==1)) tid[i][1]=i,tid[i][2]=0,tid[i][3]=i+n,col[i]=1,col[i+n]=3;
if(s[i]=='c'||(s[i]=='x'&&no[pos]==2)) tid[i][1]=i,tid[i][2]=i+n,tid[i][3]=0,col[i]=1,col[i+n]=2;
}
For(i,1,m) {
int a=q[i].a,x=q[i].x,b=q[i].b,y=q[i].y;
if(a==b&&x==y) continue;
if(tid[a][x]) {
if(tid[b][y]) {
add(tid[a][x],tid[b][y]);
int z=tid[a][x]==a?a+n:a;
int w=tid[b][y]==b?b+n:b;
add(w,z);
}
else {
int z;
For(j,1,3) if(tid[a][j]&&j!=x) z=tid[a][j];
add(tid[a][x],z);
}
}
}
For(i,1,n*2) if(!dfn[i]) tarjan(i);
int ok=1;
For(i,1,n) {
if(bl[i]==bl[i+n]) {
ok=0; break;
}
op[bl[i]]=bl[i+n];
op[bl[i+n]]=bl[i];
}
if(ok) {
For(i,1,n*2) {
for(int j=fir[i];j;j=nxt[j]) if(bl[to[j]]!=bl[i])
ADD(bl[to[j]],bl[i]);
}
tpsort();
For(i,1,n) {
if(vis[bl[i]]==1) printf("%c",'A'+col[i]-1);
else printf("%c",'A'+col[i+n]-1);
}
puts("");
return 0;
}
}
puts("-1");
return 0;
}