D44 2-SAT+前缀优化+二分 CF587D Duff in Mafia

视频链接:D44 2-SAT+前缀优化+二分 CF587D Duff in Mafia_哔哩哔哩_bilibili

 

 

 

CF587D Duff in Mafia - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;

const int N=500010;
int head[N],idx,ne[N*2],to[N*2];
void add(int x,int y){
  to[++idx]=y;ne[idx]=head[x],head[x]=idx;
}
int dfn[N],low[N],stk[N],scc[N],top,tim,cnt;
int n,m,x0,x1,p0,p1,pp0,pp1,num;
struct E{int x,y,col,val;}e[N]; //读入的边
struct V{int col,id;};
vector<V> v[N]; //端点上的边
bool cmpcol(V x,V y){return x.col<y.col;}

void tarjan(int x){
  dfn[x]=low[x]=++tim;
  stk[++top]=x;
  for(int i=head[x];i;i=ne[i]){
    int y=to[i];
    if(!dfn[y]){ //若y尚未访问
      tarjan(y);
      low[x]=min(low[x],low[y]);
    }
    else if(!scc[y]) //若y已访问且未处理
      low[x]=min(low[x],dfn[y]);
  }
  if(low[x]==dfn[x]){ //若x是SCC的根
    ++cnt;
    for(int y=-1;y!=x;)
      scc[y=stk[top--]]=cnt;
  }
}
bool check(int mid){
  memset(head,0,sizeof(head));
  memset(dfn,0,sizeof(dfn));
  memset(scc,0,sizeof(scc));
  memset(stk,0,sizeof(stk));
  idx=tim=top=cnt=0; num=2*m;
  
  for(int i=1;i<=n;i++){
    // 相同端点的边,选这条则不选其他
    for(int j=0;j<v[i].size();j++){
      x0=v[i][j].id; x1=x0+m;
      p0=++num; p1=++num;
      add(x0,p0); //向下连
      add(p1,x1); //向下连
      if(j!=0){
        add(pp0,p0); //向右连
        add(p1,pp1); //向左连            
        add(pp0,x1); //向右下连
        add(x0,pp1); //向左下连
      }
      pp0=p0,pp1=p1; //前一个节点
    }
    // 剩下的同端点同颜色的边,不选这条则选那条
    for(int j=1;j<v[i].size();j++){
      if(v[i][j-1].col==v[i][j].col){
        add(v[i][j-1].id+m,v[i][j].id);
        add(v[i][j].id+m,v[i][j-1].id);
      }
    }    
  }
  // 边权>mid的边,不选它
  for(int i=1;i<=m;i++) if(e[i].val>mid)add(i,i+m);
  
  for(int i=1;i<=m*2;i++) if(!dfn[i]) tarjan(i);
  for(int i=1;i<=m;i++) if(scc[i]==scc[i+m])return 0;
  return 1;
}
int main(){
  scanf("%d%d",&n,&m);
  for(int i=1;i<=m;i++)
    scanf("%d%d%d%d",&e[i].x,&e[i].y,&e[i].col,&e[i].val);
  for(int i=1;i<=m;i++){
    v[e[i].x].push_back({e[i].col,i});
    v[e[i].y].push_back({e[i].col,i}); //端点上的边
  }
  for(int i=1;i<=n;i++){
    sort(v[i].begin(),v[i].end(),cmpcol); //按颜色值排序
    for(int j=2;j<v[i].size();j++)
      if(v[i][j-2].col==v[i][j].col) //一个点上超过3条同色边
        return puts("No"),0;
  }
  int l=-1,r=1e9+1,mid; //二分边权
  while(l+1<r){
    mid=(l+r)>>1;
    check(mid)?r=mid:l=mid;
  }
  if(r==1e9+1) return puts("No"),0; //例如三边成环且同色
  check(r);  //退出二分时,检测值可能不是r,故再tarjan一遍
  puts("Yes"); vector<int> v;
  for(int i=1;i<=m;i++) if(scc[i]<scc[i+m])v.push_back(i);
  printf("%d %d\n",r,v.size()); //最小权,匹配边数
  for(int i=0;i<v.size();i++)printf("%d ",v[i]);
}

 

posted @ 2024-08-15 19:42  董晓  阅读(94)  评论(0编辑  收藏  举报