• Codeforces Round #768 (Div. 1)


    \(\tt vp\) 的时候过了 \(\tt ABCD\)\(\tt E\) 以后复习了 \(\tt polya\) 再补。

    F.Making It Bipartite

    题目描述

    点此看题

    解法

    最长反链的题还能变化到这种程度,着实是没想到,看博客的大佬们能再给我推点最长反链的题么

    我的 \(\tt observation\):考虑存在奇环等价于存在三点 \(x,y,z\),满足 \(x|y,y|z\),证明可以考虑存在更长的奇环就一定存在三元环。更进一步的 \(\tt observation\):我们把满足 \(x|y\)\(x,y\) 连一条 \(x\rightarrow y\) 的边,那么二分图就等价于每个点只有入度或只有出度,因为如果一个点既有出度又有入度那么就存在 \(x\rightarrow y\rightarrow z\) 的路径了。

    为什么要转化成后面那样:因为二元限制(指入度\(/\)出度关系)是更好的,所有型限制也是更优秀的。

    考虑简化问题:如果要求删点后图没有边,观察到图是一个偏序集,所以可以求出其最长反链。

    我对偏序集的理解是:如果存在边 \(x\rightarrow y,y\rightarrow z\),那么一定存在 \(x\rightarrow z\)

    回到本题,对于 每个点只有入度或只有出度 我们可以通过拆点来把它表达成不能共存型限制,具体来说我们令 \(u'\) 点表示只有入边,\(u\) 点表示只有出边,我们来构造偏序集:

    • \(u'\rightarrow u\),表示一个点的入状态和出状态不能共存。
    • 如果原图有边 \(u\rightarrow v\),那么连 \(u'\rightarrow v'\) 表示不能同时选入状态,连 \(u\rightarrow v\) 表示不能同时选出状态,连 \(u'\rightarrow v\) 表示不能 \(u\) 选入状态 \(v\) 选出状态(要不然会出现长度为 \(4\) 的链)

    容易发现我们构造出来的还是一个偏序集,那么最长反链就是最多能保留的点数,上板子即可。

    总结

    一定要对偏序集这个条件有极强的敏感度,它有时会隐晦地出现在题目中:比如倍数关系、子串关系

    单点的二元状态考虑拆点,\(\tt 2sat\) 就是经典例子,当然它的应用当然不止于 \(\tt 2sat\),如果原图是偏序集但是限制比较奇怪,可以通过各种方法(比如拆点)构造出新的偏序集

    #include <cstdio>
    #include <iostream>
    #include <queue>
    using namespace std;
    const int M = 200005;
    const int inf = 0x3f3f3f3f;
    int read()
    {
    	int x=0,f=1;char c;
    	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
    	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
    	return x*f;
    }
    int n,m,tot,a[M],b[M],id[M][4];
    int S,T,cnt,f[M],cur[M],dis[M];
    struct edge{int v,c,next;}e[40*M];
    void add(int u,int v,int c=1)
    {
    	e[++tot]=edge{v,c,f[u]},f[u]=tot;
    	e[++tot]=edge{u,0,f[v]},f[v]=tot;
    }
    int bfs()
    {
    	for(int i=S;i<=T;i++) dis[i]=0;
    	queue<int> q;dis[S]=1;q.push(S);
    	while(!q.empty())
    	{
    		int u=q.front();q.pop();
    		if(u==T) return 1;
    		for(int i=f[u];i;i=e[i].next)
    		{
    			int v=e[i].v;
    			if(!dis[v] && e[i].c>0)
    			{
    				dis[v]=dis[u]+1;
    				q.push(v);
    			}
    		}
    	}
    	return 0;
    }
    int dfs(int u,int ept)
    {
    	if(u==T) return ept;
    	int flow=0,tmp=0;
    	for(int &i=cur[u];i;i=e[i].next)
    	{
    		int v=e[i].v;
    		if(dis[v]==dis[u]+1 && e[i].c>0)
    		{
    			tmp=dfs(v,min(ept,e[i].c));
    			if(!tmp) continue;
    			flow+=tmp;ept-=tmp;
    			e[i].c-=tmp;e[i^1].c+=tmp;
    			if(!ept) break;
    		}
    	}
    	return flow;
    }
    int dinic()
    {
    	int res=0;
    	while(bfs())
    	{
    		for(int i=S;i<=T;i++) cur[i]=f[i];
    		res+=dfs(S,inf);
    	}
    	return res;
    }
    void work()
    {
    	//initialize
    	tot=1;cnt=S=m=0;n=read();
    	for(int i=1;i<=n;i++)
    		for(int j=0;j<4;j++)
    			id[i][j]=++cnt;
    	T=++cnt;
    	for(int i=S;i<=T;i++) f[i]=0;
    	//input and build
    	for(int i=1;i<=n;i++)
    		m=max(m,a[i]=read());
    	for(int i=1;i<=m;i++) b[i]=0;
    	for(int i=1;i<=n;i++) b[a[i]]=i;
    	for(int i=1;i<=m;i++) if(b[i])
    		for(int j=i+i;j<=m;j+=i) if(b[j])
    		{
    			int x=b[i],y=b[j];
    			add(id[x][0],id[y][0+2]);
    			add(id[x][0],id[y][1+2]);
    			add(id[x][1],id[y][1+2]);
    		}
    	for(int i=1;i<=n;i++)
    	{
    		add(S,id[i][0]);add(S,id[i][1]);
    		add(id[i][0+2],T);add(id[i][1+2],T);
    		add(id[i][0],id[i][1+2]);
    	}
    	printf("%d\n",dinic()-n);
    }
    signed main()
    {
    	int T=read();
    	while(T--) work();
    }
    
  • 相关阅读:
    洛谷模板汇总
    BZOJ1787【AHOI2008】Meet紧急集合 <LCA>
    HDU3068 最长回文 <Manacher>
    UVa12345 Dynamic len(set(a[L:R])) <带修莫队>
    BZOJ2038 小Z的袜子 <莫队>
    BZOJ1103【POI2007】大都市meg <树上差分+树状数组>
    BZOJ3226【SDOI2008】校门外的区间
    BZOJ1012【JSOI2008】最大数 <线段树>
    20170918~24周总结
    BZOJ1934【SHOI2007】善意的投票 <网络流>
  • 原文地址:https://www.cnblogs.com/C202044zxy/p/15930914.html
Copyright © 2020-2023  润新知