[bzoj4514]数字配对[费用流]
2016-04-25 20:30
330 查看
今年SDOI的题,看到他们在做,看到过了一百多个人,然后就被虐惨啦。。。
果然考试的时候还是打不了高端算法,调了。。。几天
默默地yy了一个费用流构图:
源连所有点,配对的点连啊,所有点连汇。。。
后来罗爷爷提醒我这样子会wa,因为你无法保证所有点都没有超过B[I]次,too naive
正解是还要考虑到奇数/偶数个质数的数字,把它们变成可二分图,看出这个性质就OK了。。。
至于要保证费用下界的问题,这个。。我也不知道为什么我原来的方法不行
后来照着标程改的,加了一行memset就过了,一脸懵逼
又贡献了一道orzliyicheng没过的题,yeah~O(∩_∩)O
#include<cstdio> #include<algorithm> #include<cstring> #define mo 200000 #define value pri #define N 200000 #define vis flag #define ll long long #define inf 10000000000000LL using namespace std; ll maxn=34000,S,T,num,n,edgenum; ll ans,tmp; ll next ,head ,up ,flag ,vet ,pri ,from ,cost ,q ,dis ,a ,b ,c ,f ; void add(int u,int v,ll w,ll c) { //printf("%d %d %lld %lld\n",u,v,w,c); edgenum++;vet[edgenum]=v;next[edgenum]=head[u];head[u]=edgenum; pri[edgenum]=w;cost[edgenum]=c;from[edgenum]=u; } ll min(ll a,ll b) { if(a<b)return a;else return b; } bool spfa() { memset(dis,127,sizeof(dis)); memset(up,0,sizeof(up)); dis[S]=0; vis[S]=1; q[1]=S; bool yes=0;int tou=1,tail=1; while (tou<=tail) { int x=q[tou%mo];vis[x]=0;//printf("query=%d\n",x); for (int i=head[x];i;i=next[i]) if (pri[i]&&dis[vet[i]]>dis[x]+cost[i]) { //printf("vet=%d\n",vet[i]); dis[vet[i]]=dis[x]+cost[i]; up[vet[i]]=i; if (!vis[vet[i]]) vis[vet[i]]=1,tail++,q[tail%mo]=vet[i];//printf("tail=%d\n",tail); if (vet[i]==T) yes=1; } tou++; } //for(int i=0;i<=T;i++)printf("%lld ",dis[i]);printf("\n"); if (!yes) return 0; else return 1; } bool flow() { int minn=inf; for (int i=up[T];i;i=up[from[i]]) minn=min(minn,pri[i]); //printf("min==%lld %lld\n",dis[T],minn); if (tmp+dis[T]*minn<=0) { for (int i=up[T];i;i=up[from[i]]) { int ee=i+1;if(i%2==0)ee=i-1; pri[i]-=minn; pri[ee]+=minn; } ans+=minn; tmp+=dis[T]*minn; return 1; } else {ans-=(tmp/dis[T]);return 0;} } void dinic() { ans=0;tmp=0; for(int i=0;i<=T;i++)flag[i]=0; while (spfa()&&flow()); printf("%lld",ans); } ll calc(ll x) { ll ans=0; for(int i=1;i<=num;i++) while(x%pri[i]==0) { x/=pri[i];ans++; } if(x!=1)ans++;return ans; } int main() { freopen("4514.in","r",stdin); freopen("4514.out","w",stdout); scanf("%lld",&n); for(int i=1;i<=n;i++)scanf("%lld",&a[i]); for(int i=1;i<=n;i++)scanf("%lld",&b[i]); for(int i=1;i<=n;i++)scanf("%lld",&c[i]); for(ll i=2;i<=maxn;i++) { if(flag[i]==0)num++,pri[num]=i; for(int j=1;j<=num;j++) { if(pri[j]*i>maxn)break; flag[pri[j]*i]=1; if(i%pri[j]==0)break; } } for(int i=1;i<=n;i++)f[i]=calc(a[i]); S=n+1,T=n+2; for(int i=1;i<=n;i++) if(f[i]%2==1)add(S,i,b[i],0),add(i,S,0,0);else add(i,T,b[i],0),add(T,i,0,0); for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) if(a[i]%a[j]==0)if(f[i]==f[j]+1) { int u,v; if(f[i]%2==1)u=i;else u=j;if(f[i]%2==0)v=i;else v=j; add(u,v,inf,-c[i]*c[j]);//printf("%d %d\n",c[i],c[j]); add(v,u,0,c[i]*c[j]); } dinic(); }
相关文章推荐
- nyoj 37 回文字符串
- (2.2.9.1)Groovy一些前提知识
- 5--指针与数组(下)
- 无人机学习第一波
- [CODEVS1090][NOIP2013]加分二叉树(树形dp)
- 如何更改workbench的快捷键
- HDU1576 A/B (解法二)
- Educational Codeforces Round 12 D. Simple Subset
- c++第四次作业
- iOS边练边学--多线程练习的多图片下载 以及 使用第三方框架(SDWebImage)的多图片下载
- 线程同步与互斥之条件·变量
- 定时自动启动任务crontab命令用法
- poj 2373(单调队列优化dp)
- (2.2.9)Groovy 介绍
- 对UIView 实例方法的探索
- HBase总结(十一)hbase Java API 介绍及使用示例
- STEM教育 or steam教育 art
- D The Lucky Week
- HDU 1283 最简单的计算机(switch+string)
- 在一个UILabel中实现不同的字体和颜色