【Codeforces 1215F】Radio Stations(2-SAT)

题目链接

复习一下2-SAT姿势(退役那年day2T1就是2-SAT,好像我挂了10分…

2-SAT问题:每个变量取值0/1,求一组变量取值满足一些约束关系。做法是每个变量 x x x开两个点 x 1 x1 x1 x 0 x0 x0,表示变量 x x x选0/1。根据约束关系连边,有向边 ( a , b ) (a,b) (a,b)表示满足了 a a a就必须满足 b b b。跑一遍Tarjan,如果存在 x x x,使得 x 0 x0 x0 x 1 x1 x1属于同一个强联通分量,则无解。否则对每个变量 x x x x 0 x0 x0所属scc编号小则取0,否则取1,就是一组合法解。

关系举例:
x x x y y y至少选一个: ( x 0 , y 1 ) , ( y 0 , x 1 ) (x0,y1),(y0,x1) (x0,y1),(y0,x1)
x x x y y y不能同时选: ( x 1 , y 0 ) , ( y 1 , x 0 ) (x1,y0),(y1,x0) (x1,y0),(y1,x0)
选了 x x x就必须选 y y y ( x 1 , y 1 ) , ( y 0 , x 0 ) (x1,y1),(y0,x0) (x1,y1),(y0,x0)
总之对于 x , y x,y x,y的二元关系,对 x 0 , y 0 , x 1 , y 1 x0,y0,x1,y1 x0,y0,x1,y1四个变量逐一考虑,看选了一个之后是不是有另一个必须选,有就连边。单变量的话,比如 x x x必须选,那就是 ( x 0 , x 1 ) (x0,x1) (x0,x1)

【P.S:有向图tarjan里做low[x]=min(low[x],dfn[y])需要判断y在栈内…无向图不用…我一开始忘记了qwq】

回到这道题,若没有 f f f的限制就会是一个裸的2-SAT。考虑 f f f的限制,我们添加 M M M个变量,第 i i i个变量表示 [ f > = i ] [f>=i] [f>=i]。那么对于一个基站 x x x及其频率 [ L , R ] [L,R] [L,R],选择 x x x就必须 [ f > = L ] [f>=L] [f>=L]取1且 [ f > = ( R + 1 ) ] [f>=(R+1)] [f>=(R+1)]取0;同时对于这 M M M个变量之间也存在限制,若 [ f > = L + 1 ] [f>=L+1] [f>=L+1]取1,则 [ f > = L ] [f>=L] [f>=L]必须取1。这个限制关系会传递下去,所以对于 [ f > = L + 1 ] [f>=L+1] [f>=L+1]不需要考虑所有 [ f > = w ] , w < = L [f>=w],w<=L [f>=w]w<=L,只需要考虑与 [ f > = L ] [f>=L] [f>=L]之间的限制关系即可。总复杂度仍然保持线性。

#define no(x) ((x)+n)
#define val(v) (sn+(v))
const int N=2000005;
int A,sn,sp,B,n;
vector<int>S[N];
void E(int x,int y) {S[x].push_back(y);}
int num[N],low[N],scc[N],st[N],cnt,top,inc;bool in[N];
void DFS(int x)
{
  num[x]=low[x]=++cnt;
  st[++top]=x;in[x]=1;
  for(auto &y:S[x])
    {
      if(!num[y]) DFS(y),low[x]=min(low[x],low[y]);
      else if(in[y]) low[x]=min(low[x],num[y]);
    }
  if(num[x]==low[x])
    {
      scc[x]=++inc;
      while(st[top]!=x) scc[st[top]]=inc,in[st[top]]=0,top--;
      top--;in[x]=0;
    }
}
int ans[N],u,f;
void _SAT( )
{
  int i,x,y,tot=n*2;
  for(i=1;i<=tot;i++)
    if(!num[i]) DFS(i);
  for(i=1;i<=sn;i++)
    {
      x=scc[i];y=scc[no(i)];
      if(x==y) {puts("-1");return;}
      if(x<y) ans[++u]=i;
    }
  for(i=1;i<=sp;i++)
    {
      x=scc[val(i)];y=scc[no(val(i))];
      if(x==y) {puts("-1");return;}
      if(x<y) f=i;
    }
  printf("%d %d\n",u,f);
  for(i=1;i<=u;i++) printf("%d ",ans[i]);
}
int main( )
{
  read(A);read(sn);read(sp);read(B);n=sn+sp;
  int i,j,x,y,l,r;
  for(i=1;i<=A;i++)
    {
      read(x),read(y);
      E(no(x),y),E(no(y),x); //x和y必须选至少一个
    }
  for(x=1;x<=sn;x++)
    {
      read(l),read(r),r++;
      E(x,val(l)),E(no(val(l)),no(x));  //选x必须选f>=l
      if(r<=sp) E(x,no(val(r))),E(val(r),no(x)); //选x则不能选f>=(r+1)
    }
  for(i=1;i<=B;i++)
    {
      read(x);read(y);
      E(x,no(y));E(y,no(x));  //x和y不能同时选
    }
  for(i=1;i<sp;i++)
    {
      l=val(i);r=val(i+1);
      E(r,l);E(no(l),no(r));  //选f>=l+1必须选f>=l
    }
  _SAT( );
  return 0;
}
  
		 
  
      

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值