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


    【CF587D】Duff in Mafia

    题意:给你一张n个点m条边的无向图,边有颜色和边权。你要从中删去一些边,满足:

    1.任意两条删掉的边没有公共的顶点。
    2.任意两条剩余的、颜色相同的边没有公共的顶点。
    3.删去的边的边权最大值最小。

    求这个最小值,并输出方案。

    $n,mle 5 imes 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
    %d %d
    ",r,tot);
    	for(i=1;i<=tot;i++)	printf("%d ",q[i]);
    	return 0;
    }
  • 相关阅读:
    乌龟git
    CI的model层的操作
    排序算法
    linux安装教程
    linux命令Netstat
    linux压缩和解压缩
    标准库模块time,datetime
    为什么计算机时间和众多编程语言要从1970年1月1日开始算起
    模块和包的导入
    Python封装应用程序的最佳项目结构是什么?
  • 原文地址:https://www.cnblogs.com/CQzhangyu/p/8681933.html
Copyright © 2020-2023  润新知