• Luogu P4068 [SDOI2016]数字配对


    反正现在做题那么少就争取做一题写一题博客吧

    看到题目发现数字种类不多,而且结合价值的要求可以容易地想到使用费用流

    但是我们如果朴素地建图就会遇到一个问题,若(i,j)符合要求,那么给(i,j)连的应该是双向边,但双向边怎么跑网络流?

    所以我们就要考虑怎么给边定向,我们稍加观察就会发现如果(i,j)合法,(j,k)也合法,那么(i,k)显然是不合法的(分四类情况讨论一下都是不合法的)

    考虑那个整除出一个质数的条件,我们发现如果我们定义(ct_i)(a_i)的所有质因数的指数之和

    那么(i,j)满足条件当且仅当((ct_i=ct_j+1and a_j|a_i)or(ct_j=ct_i+1and a_i|a_j)),这样一来我们就可以解释上面(i,j,k)三者的关系了

    那么我们发现在有了(ct_i)之后我们就可以把数字分在两边,左边放奇数,右边放偶数,这样就构成了一个二分图,我们只从左边向右边连边即可

    然后注意一下我们用EK跑最小费用最大流时每一次增广求出的最长路一定不会大于上一次增广求出的最长路,因此直接按顺序累加即可,注意最后一次的特判,详见代码

    #include<cstdio>
    #include<queue>
    #include<iostream>
    #define int long long
    #define RI register int
    #define CI const int&
    using namespace std;
    const int N=205,INF=1e18;
    struct edge
    {
    	int to,nxt,v,c;
    }e[N*N<<2]; int n,head[N],s,t,cnt=1,a[N],b[N],c[N],ct[N];
    inline int resolve(int x,int ret=0)
    {
    	for (RI i=2;i*i<=x;++i) while (x%i==0) ++ret,x/=i; return ret+(x>1);
    }
    class Network_Flow
    {
    	private:
    		queue <int> q; int dis[N],pre[N],lst[N],cap[N]; bool vis[N];
    		#define to e[i].to
    		inline bool SPFA(CI s,CI t)
    		{
    			RI i; for (i=s;i<=t;++i) dis[i]=-INF; q.push(s);
    			cap[s]=INF; dis[s]=0; while (!q.empty())
    			{
    				int now=q.front(); vis[now]=0; q.pop();
    				for (i=head[now];i;i=e[i].nxt)
    				if (e[i].v&&dis[now]+e[i].c>dis[to])
    				{
    					dis[to]=dis[pre[to]=now]+e[lst[to]=i].c;
    					if (cap[to]=min(cap[now],e[i].v),!vis[to]) q.push(to),vis[to]=1;
    				}
    			}
    			return dis[t]!=-INF;
    		}
    		#undef to
    	public:
    		inline void addedge(CI x,CI y,CI v,CI c)
    		{
    			e[++cnt]=(edge){y,head[x],v,c}; head[x]=cnt;
    			e[++cnt]=(edge){x,head[y],0,-c}; head[y]=cnt;
    		}
    		inline int solve(CI s,CI t)
    		{
    			int ret=0,ans=0; while (SPFA(s,t))
    			{
    				if (ret+dis[t]*cap[t]>=0)
    				{
    					ret+=dis[t]*cap[t]; ans+=cap[t];
    					for (int nw=t;nw!=s;nw=pre[nw])
    					e[lst[nw]].v-=cap[t],e[lst[nw]^1].v+=cap[t];
    				} else return ans+ret/(-dis[t]);
    			}
    			return ans;
    		}
    }NF;
    signed main()
    {
    	RI i,j; for (scanf("%lld",&n),i=1;i<=n;++i) scanf("%lld",&a[i]),ct[i]=resolve(a[i]);
    	for (i=1;i<=n;++i) scanf("%lld",&b[i]); for (i=1;i<=n;++i) scanf("%lld",&c[i]);
    	for (t=n+1,i=1;i<=n;++i) if (ct[i]&1) NF.addedge(s,i,b[i],0); else NF.addedge(i,t,b[i],0);
    	for (i=1;i<=n;++i) if (ct[i]&1) for (j=1;j<=n;++j) if (!(ct[j]&1))
    	if ((ct[i]+1==ct[j]&&a[j]%a[i]==0)||(ct[j]+1==ct[i]&&a[i]%a[j]==0))
    	NF.addedge(i,j,INF,c[i]*c[j]); return printf("%lld",NF.solve(s,t)),0;
    }
    
  • 相关阅读:
    emWin 界面切换注意事项
    emWin 工程之汉字显示
    emWin 使用 GUIBuilder 放置标题 TEXT 注意
    【转】系统调用和驱动程序中相应函数的参数对应关系
    主机 & 虚拟机 & 开发板 相互通信
    电脑通过网口连接开发板
    【转】ARM交叉编译工具链
    【转】vi 写完文件保存时才发现是 readonly
    【转】ubuntu 12.04下如何开启 NFS 服务 & 设置
    安装完打开 eclipse 提示 JVM 版本较低
  • 原文地址:https://www.cnblogs.com/cjjsb/p/11566672.html
Copyright © 2020-2023  润新知