zoukankan      html  css  js  c++  java
  • Codeforces 587D

    Codeforces 题面传送门 & 洛谷题面传送门

    2-SAT hot tea。

    首先一眼二分答案,我们二分答案 (mid),那么问题转化为,是否存在一个所有边权都 (le mid) 的集合 (S),满足 (S)​ 中任意两条边的端点互不相同,并且没有选择的选择的边每种颜色的边两两之间的端点也互不相同。

    乍一看这个问题看似无法解决。但不难发现每条边只有两种状态——选或不选,也就是说我们考虑将每条边拆成两个点 (x_i)​ 和 (lnot x_i)​,分别表示边 (i)​ 被选入集合 (S)​ 中和边 (i)​ 没有被选入集合 (S) 然后跑 2-SAT​,那么考虑这样建图:

    • 对于权值 (>mid)​ 的边我们连一条 (x_i olnot x_i) 的边,这样只要选择了 (x_i) 就必定会推出 (lnot x_i) 也为真,也就导致了矛盾,因此这样连边就强制要求 (lnot x_i) 必须为真。
    • 对于两条有公共端点的边 (i,j),如果它们同时被纳入集合 (S) 中就会导致 (S) 中的边构不成匹配,因此如果 (x_i) 为真必然可以推出 (lnot x_j) 为真,因此我们考虑连边 (x_i olnot x_j),同理逆否命题 (x_j olnot x_i)
    • 类似地,对于两条有公共端点且颜色相同的边 (i,j),如果它们同时不选,就会导致没选入 (S) 的边不合法,因此我们连边 (lnot x_i o x_j,lnot x_j o x_i)

    这样暴力连边复杂度是 (mathcal O(m^2)) 的,无法通过,考虑优化。以第二类边为例,我们考虑枚举两条边的公共点 (i)​,那么我们考虑将所有与 (i) 相连的边排成一列,设为 (e_1,e_2,cdots,e_k),那么我们需要对于所有 (1le p,qle k,p e q),连边 (x_p olnot x_q),不难发现这可以用前后缀优化建图优化,具体优化方案如下:

    对于第三类边也按照同样方式优化一下即可。这样边数即可降到 (mathcal O(m)) 级别。然后跑 tarjan,如果发现 (exists i)(x_i)(lnot x_i) 在同一个强连通分量中说明 (mid) 不合法,否则由于 tarjan 强连通分量编号按照缩点后拓扑序,可通过 (x_i)(lnot x_i) 所在强连通分量大小关系判断 (i) 是否被选入集合 (S)

    时间复杂度 (mathcal O((n+m)log n)),为了减少常数可先把二、三类边建出来并保存一个备份,这样每次 check 时只需建一类边即可,不必每次二分都把整张图重新建出来。

    const int MAXN=5e4;
    const int MAXM=5e4;
    const int MAXV=5e5;
    const int MAXE=2e6;
    int n,m,mx,ncnt=0;
    struct edge{int u,v,c,t;} e[MAXM+5];
    vector<int> g[MAXN+5];
    vector<pii> col[MAXN+5];
    int hd[MAXV+5],to[MAXE+5],nxt[MAXE+5],ec=0;
    int _hd[MAXV+5],_ec=0;
    void adde(int u,int v){to[++ec]=v;nxt[ec]=hd[u];hd[u]=ec;}
    int dfn[MAXV+5],low[MAXV+5],stk[MAXV+5],tp=0;
    int bel[MAXV+5],cmp=0,tim=0;bool vis[MAXV+5];
    void tarjan(int x){
    	dfn[x]=low[x]=++tim;stk[++tp]=x;vis[x]=1;
    	for(int e=hd[x];e;e=nxt[e]){
    		int y=to[e];
    		if(!dfn[y]) tarjan(y),chkmin(low[x],low[y]);
    		else if(vis[y]) chkmin(low[x],dfn[y]);
    	}
    	if(dfn[x]==low[x]){
    		int o;cmp++;do{
    			o=stk[tp--];bel[o]=cmp;vis[o]=0;
    		} while(o^x);
    	}
    }
    bool check(int mid){
    	for(int i=1;i<=ncnt;i++) hd[i]=_hd[i];ec=_ec;
    	memset(dfn,0,sizeof(dfn));memset(low,0,sizeof(low));tim=cmp=tp=0;
    	for(int i=1;i<=m;i++) if(e[i].t>mid) adde(i,i+m);
    	for(int i=1;i<=ncnt;i++) (!dfn[i]&&(tarjan(i),0));
    	for(int i=1;i<=m;i++) if(bel[i]==bel[i+m]) return 0;
    	return 1;
    }
    int main(){
    	scanf("%d%d",&n,&m);
    	for(int i=1;i<=m;i++){
    		scanf("%d%d%d%d",&e[i].u,&e[i].v,&e[i].c,&e[i].t);
    		chkmax(mx,e[i].t);g[e[i].u].pb(i);g[e[i].v].pb(i);
    		col[e[i].u].pb(mp(e[i].c,i));col[e[i].v].pb(mp(e[i].c,i));
    	} ncnt=m<<1;
    	for(int i=1;i<=n;i++) sort(col[i].begin(),col[i].end());
    	for(int i=1;i<=n;i++){
    		vector<int> pre(g[i].size()),suf(g[i].size());
    		for(int j=0;j<pre.size();j++) pre[j]=++ncnt;
    		for(int j=0;j<suf.size();j++) suf[j]=++ncnt;
    		for(int j=1;j<pre.size();j++) adde(pre[j-1],pre[j]);
    		for(int j=1;j<suf.size();j++) adde(suf[j],suf[j-1]);
    		for(int j=0;j<pre.size();j++) adde(g[i][j],pre[j]);
    		for(int j=0;j<suf.size();j++) adde(suf[j],g[i][j]+m);
    		for(int j=1;j<pre.size();j++) adde(pre[j-1],g[i][j]+m),adde(g[i][j],suf[j-1]);
    	}
    	for(int i=1;i<=n;i++){
    		for(int l=0,r;l<col[i].size();l=r){
    			r=l;while(r<col[i].size()&&col[i][r].fi==col[i][l].fi) ++r;
    			vector<int> pre(r-l),suf(r-l);
    			for(int j=0;j<pre.size();j++) pre[j]=++ncnt;
    			for(int j=0;j<suf.size();j++) suf[j]=++ncnt;
    			for(int j=1;j<pre.size();j++) adde(pre[j-1],pre[j]);
    			for(int j=1;j<suf.size();j++) adde(suf[j],suf[j-1]);
    			for(int j=0;j<pre.size();j++) adde(pre[j],col[i][j+l].se);
    			for(int j=0;j<suf.size();j++) adde(col[i][j+l].se+m,suf[j]);
    			for(int j=1;j<pre.size();j++) adde(col[i][j-1+l].se+m,pre[j]),adde(suf[j],col[i][j-1+l].se);
    		}
    	}
    	for(int i=1;i<=ncnt;i++) _hd[i]=hd[i];_ec=ec;
    	int l=0,r=mx,p=-1;
    	while(l<=r){
    		int mid=l+r>>1;
    		if(check(mid)) p=mid,r=mid-1;
    		else l=mid+1;
    	} if(!~p) puts("No");
    	else{
    		printf("Yes
    %d ",p);check(p);vector<int> vec;
    		for(int i=1;i<=m;i++) if(bel[i]<bel[i+m]) vec.pb(i);
    		printf("%d
    ",vec.size());for(int x:vec) printf("%d ",x);
    	}
    	return 0;
    }
    
  • 相关阅读:
    WCF技术剖析之二十六:如何导出WCF服务的元数据(Metadata)[实现篇]
    事件(Event),绝大多数内存泄漏(Memory Leak)的元凶[上篇]
    谈谈关于MVP模式中VP交互问题
    如何通过VPC在本机搭建局域网
    谈谈分布式事务之三: System.Transactions事务详解[下篇]
    WCF版的PetShop之二:模块中的层次划分[提供源代码下载]
    实践重于理论——创建一个监控程序探测WCF的并发处理机制
    WCF技术剖析之二十五: 元数据(Metadata)架构体系全景展现[WS标准篇]
    WCF技术剖析之三十一: WCF事务编程[中篇]
    微软将结束对Windows 2000、XP和Vista部份版本的技术支持
  • 原文地址:https://www.cnblogs.com/ET2006/p/Codeforces-587D.html
Copyright © 2011-2022 走看看