bzoj2400

时间:2022-10-03 17:20:50

首先xor类的题目一定要逐位考虑,因为位位之间是不相互影响的
逐位考虑每个点是0还是1,这就转化成了一个这样一个问题
对于每个点可以选择属于S集合(这位是0)或T集合(这位是1)
某些的点对(一条边的两端)属于不同集合会产生一个附加值1(边权)
现在要是附加值最小(边权当前位为1的边最少),并且属于S集合的尽可能多(点权当前位为1尽可能少)
显然第一个问题是一个最小割的问题,对于已经确定这位是什么的点i来说
是0则连边s-->i 容量inf,是1则连边i-->t容量是inf,因为这些点当前位是确定的
然后对一条边(u,v),连边u-->v v-->u 容量都是1,最小割就是最小附加值
这里为什么不用给不确定的点向s,t连边呢?这个我也没有想清楚
反正直观的感受,当一条路径直接或间接连接着则两点一个确定为0,一个确定为1,
那么这条路径最少会产生附加值1,一定要被割断,否则这条路径我一定能使它不产生附加值
下面考虑让属于S的点(这位是0的点)尽可能多
考虑在残流网络上是不存在增广路的,也就是对于一个未确定点i,如果这个点能属于S
那么我从s到i给一个流量,这个流量从点i一定不能流向t,否则就会增大边权和
因此我们只要对于每个未确定的点i在残流网络上顺着剩余容量大于0的边dfs,
只要到不了t,那么这个点就可以属于S(这位为0),否则不行
这样就解决了

 const inf=;
type node=record
point,next,flow:longint;
end; var edge:array[..] of node;
p,cur,d,a,pre,h,numh:array[..] of longint;
v:array[ ..] of boolean;
x,y:array[..] of longint;
i,j,n,m,k,len,t,b:longint;
ans1,ans2:int64; function min(a,b:longint):longint;
begin
if a>b then exit(b) else exit(a);
end; procedure add(x,y,f:longint);
begin
inc(len);
edge[len].point:=y;
edge[len].flow:=f;
edge[len].next:=p[x];
p[x]:=len;
end; function sap:longint;
var u,i,j,tmp,neck,q:longint;
begin
fillchar(h,sizeof(h),);
fillchar(numh,sizeof(numh),);
numh[]:=t+;
u:=;
for i:= to t do
cur[i]:=p[i];
neck:=inf;
sap:=;
while h[]<t+ do
begin
d[u]:=neck;
i:=cur[u];
while i<>- do
begin
j:=edge[i].point;
if (edge[i].flow>) and (h[u]=h[j]+) then
begin
pre[j]:=u;
cur[u]:=i;
neck:=min(neck,edge[i].flow);
u:=j;
if u=t then
begin
sap:=sap+neck;
while u<> do
begin
u:=pre[u];
j:=cur[u];
dec(edge[j].flow,neck);
inc(edge[j xor ].flow,neck);
end;
neck:=inf;
end;
break;
end;
i:=edge[i].next;
end;
if i=- then
begin
dec(numh[h[u]]);
if numh[h[u]]= then exit;
tmp:=t;
q:=-;
i:=p[u];
while i<>- do
begin
j:=edge[i].point;
if edge[i].flow> then
if h[j]<tmp then
begin
tmp:=h[j];
q:=i;
end;
i:=edge[i].next;
end;
h[u]:=tmp+;
cur[u]:=q;
inc(numh[h[u]]);
if u<> then
begin
u:=pre[u];
neck:=d[u];
end;
end;
end;
end; function dfs(x:longint):boolean;
var i,y:longint;
begin
v[x]:=true;
if x=t then exit(true);
i:=p[x];
while i<>- do
begin
y:=edge[i].point;
if (edge[i].flow>) and not v[y] then
if dfs(y) then exit(true);
i:=edge[i].next;
end;
exit(false);
end; begin
readln(n,m);
b:=;
for i:= to n do
begin
readln(a[i]);
if (a[i]>) and (trunc(ln(a[i])/ln())>b) then
b:=trunc(ln(a[i])/ln()); //显然比以确定点的最大位数还大的位直接取0即可
if a[i]> then ans2:=ans2+a[i];
end;
for i:= to m do
readln(x[i],y[i]);
t:=n+;
for i:= to b do
begin
len:=-;
fillchar(p,sizeof(p),);
for j:= to n do
if a[j]>= then
begin
if a[j] and ( shl i)= then
begin
add(,j,inf);
add(j,,);
end
else begin
add(j,t,inf);
add(t,j,);
end;
end;
for j:= to m do
begin
add(x[j],y[j],);
add(y[j],x[j],);
add(y[j],x[j],);
add(x[j],y[j],);
end;
ans1:=ans1+int64(sap)*int64( shl i); // shl i是这位位权
for j:= to n do
if a[j]< then
begin
fillchar(v,sizeof(v),false);
if dfs(j) then ans2:=ans2+ shl i; //能访问到t则这个点当前位不能为0
end;
end;
writeln(ans1);
writeln(ans2);
end.

相关文章