费用流
——写这一篇文章的原因不是因为不会这一题,而是我发现我多年以来(?)滚动数组都用错了。
如果令可以配对的两个点连边,仔细研究可以发现图中一定没有环。图一定是森林。于是可以构出二分图,用费用流解决问题。
然后我就TLE飞了,调了一个小时,才发现原因,好气啊。
我想说的是,我从开始用滚动数组,直到现在,滚动数组的姿势都错啦!
假设head是头指针,tail是尾指针,队列为q[N]
原本我习惯于 :
for(int head=0, tail=1; head!=tail; head++)
{
if(head==N)head=0;
...
if(tail==N)tail=0;
}
然而这是错的!用了这么久竟然没发现。。。(之前过的题数据是有多弱?)
错误的原因是当head=N到上界,跳转至head=0时,此时tail也可能为0!这时候应该退出队列,然而我的代码没考虑这一点。。。
正确姿势:
for(int head=0, tail=1; ; head++)
{
if(head==N)head=0;
if(head==tail)break; //先把head放到正确的位置再判
...
if(tail==N)tail=0;
}
依稀记得当年初学算法,做最短路的一道题时,用滚动数组就是过不去,改常规数组就过了。当时没怎么在意,周围的神犇也没有看出我的错误(确实不是那么容易看出来)。这个问题就遗留到现在了。。。
毕竟这是我个人的傻逼错误。。。看这篇文章的神犇如果觉得这太弱智,笑笑就好。
接下来是本题代码。
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 205
#define P 33333
#define ll long long
#define cmin(u,v) (u)>(v)?(u)=(v):0
using namespace std;
namespace runzhe2000
{
struct edge{int next,to,flow;ll cost;}e[N*N*5];
const int INF = 1<<29, S = N-3, T = N-2;
ll cost[N], mc;
int ecnt = 1, a[N], b[N], c[N], prime[P], pcnt, last[N], q[N], from[N];
int belong[N], s1[N], t1, s2[N], t2, mf;
bool notprime[P], inq[N];
void addedge(int a, int b, int f, ll v)
{
e[++ecnt] = (edge){last[a], b, f, v};
last[a] = ecnt;
e[++ecnt] = (edge){last[b], a, 0, -v};
last[b] = ecnt;
}
void init()
{
for(int i = 2; i < P; i++)
{
if(!notprime[i])
prime[++pcnt] = i;
for(int j = 1; j <= pcnt && 1ll*prime[j]*i<P; j++)
{
notprime[prime[j]*i] = 1;
if(i%prime[j] == 0)break;
}
}
}
bool judge(int x, int y)
{
if(!x || !y || (x % y && y % x))return false;
int tmp = max(x,y) / min(y,x);
if(tmp < P)return !notprime[tmp];
for(int i = 1; i <= pcnt; i++)
if(tmp % prime[i] == 0)return false;
return true;
}
bool EK()
{
memset(cost,127,sizeof(cost));
cost[S] = 0;
q[0] = S;
from[T] = 0;
for(int head=0, tail=1; ; head++)
{
if(head >= N) head = 0;
if(head == tail)break; //好气啊
int x = q[head];
inq[x] = 0;
for(int i = last[x]; i; i = e[i].next)
{
int y = e[i].to;
if(cost[x] + e[i].cost < cost[y] && e[i].flow)
{
cost[y] = cost[x] + e[i].cost;
from[y] = i;
if(!inq[y])
{
inq[y] = 1;
q[tail++] = y;
if(tail >= N) tail = 0;
}
}
}
}
if(!from[T])return false;
int f = INF;
for(int i = from[T]; i; i = from[e[i^1].to])
cmin(f, e[i].flow);
for(int i = from[T]; i; i = from[e[i^1].to])
e[i].flow -= f, e[i^1].flow += f;
if(mc + f*cost[T] > 0)
{
mf += (-mc) / cost[T];
return false;
}
else
{
mc += f*cost[T];
mf += f;
return true;
}
}
void main()
{
init();
int n;
scanf("%d",&n);
for(int i = 1; i <= n; i++)scanf("%d",&a[i]);
for(int i = 1; i <= n; i++)scanf("%d",&b[i]);
for(int i = 1; i <= n; i++)scanf("%d",&c[i]);
for(int i = 1; i <= n; i++)
{
int tmp = a[i], cnt = 0;
for(int j = 1; j <= pcnt && tmp != 1; j++)
{
while(tmp % prime[j] == 0)
tmp /= prime[j], ++cnt;
}
if(tmp != 1)++cnt;
if(cnt & 1)s1[++t1] = i, belong[i] = 1, addedge(S,i,b[i],0);
else s2[++t2] = i, belong[i] = 2, addedge(i,T,b[i],0);
}
for(int i = 1; i <= t1; i++)
{
int x = s1[i];
for(int j = 1; j <= t2; j++)
{
int y = s2[j];
if(judge(a[x],a[y]))
{
if(belong[x] == 1)
addedge(x, y, INF, -1ll*c[x]*c[y]);
else
addedge(y, x, INF, -1ll*c[x]*c[y]);
}
}
}
while(EK());
printf("%d\n",mf);
}
}
int main()
{
runzhe2000::main();
}