【CF587D】Duff in Mafia 二分+前缀优化建图+2-SAT
2018-03-31 13:57
911 查看
【CF587D】Duff in Mafia
题意:给你一张n个点m条边的无向图,边有颜色和边权。你要从中删去一些边,满足:1.任意两条删掉的边没有公共的顶点。
2.任意两条剩余的、颜色相同的边没有公共的顶点。
3.删去的边的边权最大值最小。
求这个最小值,并输出方案。
$n,m\le 5\times 10^4$
题解:首先二分答案。我们二分删去边权的最大值mid,则所有>mid的边都要保留,其余的可以保留也可以删去。因为每条边有删或不删两种状态,所以容易转化为2-SAT模型。于是题中条件可以转化成若干个形如这样的约束:在一个边集中,最多有一条边被删(不删)。
这是一个经典的2-SAT模型,有一个常用办法:前缀优化建图。
我们把集合中的元素排成一排,然后新建两排节点,第一排的第i个点代表在前i个元素中有一个元素被选择了,第二排的第i个点代表前i个元素中一个被选的都没有。假如某个元素选的状态是$x_i$,不选的状态是$x_i'$,对应的前缀分别是$S_i$和$S_i'$,那么:
前缀的传递:$S_{i-1}->S_i$,$S_i'->S_{i-1}'$
用当前元素更新当前前缀:$x_i->S_i,S_i'->x_i'$
用前缀来约束当前元素:$S_{i-1}->x_i',x_i->S_{i-1}'$
记住,一共6条边哦!
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #include <vector> #define A(_) (((_)<<1)-1) #define B(_) ((_)<<1) using namespace std; const int maxn=500010; int n,m,mid,tot,cnt,top,sum; int to[2000010],nxt[2000010],head[maxn],dep[maxn],q[maxn],bel[maxn],low[maxn],sta[maxn],ins[maxn]; vector<int> v[maxn]; vector<int>::iterator it; struct edge { int a,b,c,d; }p[maxn]; inline int rd() { int ret=0,f=1; char gc=getchar(); while(gc<'0'||gc>'9') {if(gc=='-') f=-f; gc=getchar();} while(gc>='0'&&gc<='9') ret=ret*10+(gc^'0'),gc=getchar(); return ret*f; } inline void add(int a,int b) { to[cnt]=b,nxt[cnt]=head[a],head[a]=cnt++; } bool cmp(const int &a,const int &b) { return p[a].c<p[b].c; } inline void work1() { int i; for(i=1;i<=q[0];i++) { add(A(q[i]),A(i)+tot),add(B(i)+tot,B(q[i])); if(i!=1) { add(A(i-1)+tot,A(i)+tot),add(B(i)+tot,B(i-1)+tot); add(A(i-1)+tot,B(q[i])),add(A(q[i]),B(i-1)+tot); } } tot+=q[0]<<1,q[0]=0; } inline void work2() { int i; for(i=1;i<=q[0];i++) { add(B(q[i]),B(i)+tot),add(A(i)+tot,A(q[i])); if(i!=1) { add(B(i-1)+tot,B(i)+tot),add(A(i)+tot,A(i-1)+tot); add(B(i-1)+tot,A(q[i])),add(B(q[i]),A(i-1)+tot); } } tot+=q[0]<<1,q[0]=0; } void tarjan(int x) { dep[x]=low[x]=++dep[0],ins[x]=1,sta[++top]=x; for(int i=head[x];i!=-1;i=nxt[i]) { if(!dep[to[i]]) tarjan(to[i]),low[x]=min(low[x],low[to[i]]); else if(ins[to[i]]) low[x]=min(low[x],dep[to[i]]); } if(dep[x]==low[x]) { sum++; int t; do { t=sta[top--],ins[t]=0,bel[t]=sum; }while(t!=x); } } inline bool check() { int i; tot=m<<1,cnt=0,memset(head,-1,sizeof(head)); for(i=1;i<=m;i++) if(p[i].d>mid) add(B(i),A(i)); for(i=1;i<=n;i++) { for(it=v[i].begin();it!=v[i].end();it++) { if(q[0]&&p[*it].c!=p[q[q[0]]].c) work1(); q[++q[0]]=*it; } work1(); for(it=v[i].begin();it!=v[i].end();it++) q[++q[0]]=*it; work2(); } memset(dep,0,sizeof(dep)),sum=0; for(i=1;i<=tot;i++) if(!dep[i]) tarjan(i); for(i=1;i<=tot;i+=2) if(bel[i]==bel[i+1]) return 0; return 1; } int main() { //freopen("cf587D.in","r",stdin); n=rd(),m=rd(); int i,l=0,r=0,flag=0; for(i=1;i<=m;i++) { p[i].a=rd(),p[i].b=rd(),p[i].c=rd(),p[i].d=rd(),r=max(r,p[i].d+1); v[p[i].a].push_back(i),v[p[i].b].push_back(i); } for(i=1;i<=n;i++) sort(v[i].begin(),v[i].end(),cmp); while(l<r) { mid=(l+r)>>1; if(check()) r=mid,flag=1; else l=mid+1; } if(!flag) { puts("No"); return 0; } mid=r,check(); tot=0; for(i=1;i<=m;i++) if(bel[A(i)]>bel[B(i)]) q[++tot]=i; printf("Yes\n%d %d\n",r,tot); for(i=1;i<=tot;i++) printf("%d ",q[i]); return 0; }
相关文章推荐
- [二分答案 2-SAT验证 前后缀优化建图] Codeforces 587D #326 (Div. 1) D. Duff in Mafia
- [线段树 & 前缀 优化建图 二分 2-SAT] CF Gym100159 facebook-hacker-cup-2012 I. Unfriending
- [二分答案 2-SAT验证 前缀后缀优化建图 线段树优化建图] Codeforces gym 100159 Facebook Hacker Cup 2012 I. Unfriending
- [二分 前缀优化建图 2-SAT] Codeforces 587D. Duff in Mafia
- Codeforces Round #274 (Div. 2) E:Riding in a Lift DP + 前缀优化
- BZOJ.3495.[PA2010]Riddle(2-SAT 前缀优化建图)
- 违禁词过滤完整设计与优化(前缀匹配、二分查找)
- HDU 1815 Building roads 二分+2-sat充分理解建图
- 一个有趣的题目【二分答案,2-SAT,线段树优化】
- NKOJ 4244 (HAOI 2008) 木棍分割 (二分答案+DP+单调队列+前缀和优化+滚动数组)
- [UOJ210]-寻找罪犯-前缀边优化2-SAT
- AtCoder Regular Contest 069 F - Flags 2-SAT+线段树优化建图
- HAOI2008 木棍分割 二分答案 前缀和优化 单调队列 滚动数组
- [UOJ]210 寻找罪犯 2-Sat 前缀和优化
- BZOJ 1044: [HAOI2008]木棍分割 DP,前缀和优化,二分答案
- 1044: [HAOI2008]木棍分割 二分答案+DP+前缀和优化
- [BZOJ1044][HAOI2008]木棍分割 二分+贪心+dp+前缀和优化
- [BZOJ]3495 Riddle 2-Sat 前缀和优化
- noip2011 聪明的质监员 (二分+前缀和处理+读入优化)
- 【前后缀优化建图+2-SAT】BZOJ3495(PA2010)[Riddle]题解