[二分答案 2-SAT验证 前后缀优化建图] Codeforces 587D #326 (Div. 1) D. Duff in Mafia

大概题意是删去一组边 这组边是一个匹配
剩下的边 同一颜色的一组边也形成一个匹配
使删去的边最大值最小
这个考虑二分答案 然后检验

  • 大于答案的边必然保留
  • 同一个点连的同一颜色的边最多保留一条
  • 同一个点连的边最多删除一条

这个某些边集最多删一条怎么处理
把他排成一排 那么一个点选了 他的前面都不能选 后面都不能选 那么建前后缀使得边数降到 O(n) 要是裸建图的话是 O(n2)

红点是表示前缀后缀辅助点
灵魂画作

#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<vector>
using namespace std;
typedef long long ll;

inline char nc(){
  static char buf[100000],*p1=buf,*p2=buf;
  if (p1==p2) { p2=(p1=buf)+fread(buf,1,100000,stdin); if (p1==p2) return EOF; }
  return *p1++;
}
inline void read(int &x){
  char c=nc(),b=1;
  for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1;
  for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b;
}

const int N=500005;

struct edge{
  int u,v,next;
}G[N*10];
int head[N],inum;
int _head[N],tmp;
inline void add(int u,int v){
  int p=++inum; G[p].u=u; G[p].v=v; G[p].next=head[u]; head[u]=p;
}
int pre[N],low[N],clk;
int scc[N],cnt; int Stack[N],pnt;
#define V G[p].v
inline void dfs(int u){
  pre[u]=low[u]=++clk; Stack[++pnt]=u;
  for (int p=head[u];p;p=G[p].next)
    if (!pre[V])
      dfs(V),low[u]=min(low[u],low[V]);
    else if (!scc[V])
      low[u]=min(low[u],pre[V]);
  if (low[u]==pre[u]){
    ++cnt;
    while (Stack[pnt]!=u) scc[Stack[pnt--]]=cnt; scc[Stack[pnt--]]=cnt; 
  }
}

int n,m,Tot;
int y[N],vc;
int size[N],sum[N];
inline int pf(int idx,int x){ return (m<<1)+(sum[idx-1]<<1)+x; }
inline int sf(int idx,int x){ return (m<<1)+(sum[idx-1]<<1)+size[idx]+x; }

inline bool Tarjan(){
  for (int i=0;i<Tot;i++) pre[i]=low[i]=scc[i]=0; pnt=cnt=clk=0;
  for (int i=0;i<Tot;i++)
    if (!pre[i])
      dfs(i);
  for (int i=0;i<2*m;i+=2)
    if (scc[i]==scc[i^1])
      return 0;
  return 1;
}

int us[N],vs[N],ts[N],cs[N];

inline bool check(int x){
  if (x==3)
    x=3;
  for (int i=0;i<m;i++)
    if (ts[i]>x)
      add(i<<1,i<<1|1);
  return Tarjan();
}

int sx[N],icnt;
struct abcd{
  int v,c,idx;
  abcd(int v=0,int c=0,int idx=0):v(v),c(c),idx(idx) { }
  bool operator < (const abcd &B) const{
    return v==B.v?c<B.c:v<B.v;
  }
}edges[N]; int ecnt;

int ans[N],kans;

int main(){
  freopen("t.in","r",stdin);
  freopen("t.out","w",stdout);
  read(n); read(m);
  for (int i=0;i<m;i++) read(us[i]),read(vs[i]),read(cs[i]),read(ts[i]),sx[++icnt]=cs[i];
  sort(sx+1,sx+icnt+1); icnt=unique(sx+1,sx+icnt+1)-sx-1;
  for (int i=0;i<m;i++){
    cs[i]=lower_bound(sx+1,sx+icnt+1,cs[i])-sx;
    edges[++ecnt]=abcd(us[i],cs[i],i);
    edges[++ecnt]=abcd(vs[i],cs[i],i);
  }
  sort(edges+1,edges+ecnt+1);
  int pnt=1;
  while (pnt<=ecnt){
    ++vc; y[size[vc]++]=edges[pnt++].idx;
    while (pnt<=ecnt && edges[pnt].v==edges[pnt-1].v && edges[pnt].c==edges[pnt-1].c)
      y[size[vc]++]=edges[pnt++].idx;
    for (int j=0;j<size[vc];j++){
      if (j-1>=0) add(pf(vc,j),pf(vc,j-1));
      add(pf(vc,j),y[j]<<1);
      if (j+1<size[vc]) add(sf(vc,j),sf(vc,j+1));
      add(sf(vc,j),y[j]<<1);
    }
    for (int j=0;j<size[vc];j++){
      if (j-1>=0) add(y[j]<<1|1,pf(vc,j-1));
      if (j+1<size[vc]) add(y[j]<<1|1,sf(vc,j+1));
    }
    sum[vc]=sum[vc-1]+size[vc];
  }
  pnt=1;
  while (pnt<=ecnt){
    ++vc; y[size[vc]++]=edges[pnt++].idx;
    while (pnt<=ecnt && edges[pnt].v==edges[pnt-1].v)
      y[size[vc]++]=edges[pnt++].idx;
    for (int j=0;j<size[vc];j++){
      if (j-1>=0) add(pf(vc,j),pf(vc,j-1));
      add(pf(vc,j),y[j]<<1|1);
      if (j+1<size[vc]) add(sf(vc,j),sf(vc,j+1));
      add(sf(vc,j),y[j]<<1|1);
    }
    for (int j=0;j<size[vc];j++){
      if (j-1>=0) add(y[j]<<1,pf(vc,j-1));
      if (j+1<size[vc]) add(y[j]<<1,sf(vc,j+1));
    }
    sum[vc]=sum[vc-1]+size[vc];
  }
  Tot=(m<<1)+(sum[vc]<<1);
  for (int i=0;i<2*m;i++) _head[i]=head[i]; tmp=inum;
  if (!Tarjan()) { printf("No\n"); return 0; }
  int L=-1,R=1<<30,MID;
  while (L+1<R){
    if (check(MID=(L+R)>>1))
      R=MID;
    else
      L=MID;
    for (int i=0;i<2*m;i++) head[i]=_head[i]; inum=tmp;
  }
  check(R);
  printf("Yes\n");
  for (int i=0;i<m;i++){
    ans[i]=scc[i<<1|1]<scc[i<<1];
    if (!ans[i]) kans++;
  }
  printf("%d %d\n",R,kans);
  for (int i=0;i<m;i++) if (!ans[i]) printf("%d ",i+1);
  return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值