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

来源:互联网 发布:java中main是什么意思 编辑:程序博客网 时间:2024/05/16 07:30

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

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

这个某些边集最多删一条怎么处理
把他排成一排 那么一个点选了 他的前面都不能选 后面都不能选 那么建前后缀使得边数降到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].vinline 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 0
原创粉丝点击