【CF587D】Duff in Mafia 二分+前缀优化建图+2-SAT

【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 
#include 
#include 
#include 
#include 
#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 v[maxn];
vector::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].cmid)	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>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;
}

转载于:https://www.cnblogs.com/CQzhangyu/p/8681933.html

你可能感兴趣的:(数据结构与算法)