1 条题解

  • 0
    @ 2026-1-29 20:25:58

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

    #include<bits/stdc++.h>
    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]);
    }
    
    • 1

    D44 2-SAT+前缀优化+二分 [CF587D] Duff in Mafia(无法评测)

    信息

    ID
    4728
    时间
    6000ms
    内存
    256MiB
    难度
    10
    标签
    递交数
    6
    已通过
    1
    上传者