题目大意
~~~~~~
有一幅竞赛图(n<=200),其中一些边未定向(
s
[
i
]
[
j
]
=
1
s[i][j]=1
s[i][j]=1 且
s
[
j
]
[
i
]
=
0
s[j][i]=0
s[j][i]=0 表示一条边从
i
i
i 到
j
j
j,
s
[
i
]
[
j
]
=
s
[
j
]
[
i
]
=
2
s[i][j]=s[j][i]=2
s[i][j]=s[j][i]=2 表示未定向)。现在你要把这些边定向,使得图的权值最大。权值用下面的算法来计算:
题解
~~~~~~ 据说你们都能很顺利地直接推出式子。。。果然是我太菜了吗 QAQ
~~~~~~
考虑容斥,首先先把所有四元环加一次(即
A
n
4
A_n^4
An4),然后把不该加的去掉。不该加的环一定存在某个点连了两条出边,且如果这个环是
A
n
s
Ans
Ans 不加不减的,那么它有且仅有一个这样的点;如果是
A
n
s
Ans
Ans 要减的,那么它就会有两个这样的点。
~~~~~~
于是我们只要枚举一个点的两条出边,再枚举另一个点,这样来代表一个不该加的环,然后给
A
n
s
Ans
Ans 减 8。如果这个环是
A
n
s
Ans
Ans 不加不减的,那么它刚好被全部减掉;如果这个环是
A
n
s
Ans
Ans 要减的,那么它就被减了两次,刚好成为负数。于是得出了下面的式子:
A
n
s
=
A
n
4
−
∑
i
=
1
n
(
d
e
g
[
i
]
2
)
∗
(
n
−
3
)
∗
8
Ans=A^4_n-\sum_{i=1}^n \binom{deg[i]}{2}*(n-3)*8
Ans=An4−i=1∑n(2deg[i])∗(n−3)∗8
~~~~~~
(注:牛客上面的式子是错的)
~~~~~~ 于是可以发现答案只跟点的出度有关,并且 ∑ ( d e g [ i ] 2 ) \sum \binom{deg[i]}{2} ∑(2deg[i]) 越小越好。这个可以费用流,左边一排点表示未定向的边,右边两排点表示原图的点,右边的 i i i 与 i ′ i' i′ 之间连若干条边,表示点 i i i 每次度数 +1 的费用增量(类似于动态加边那样,但是不用动态加边也能过)。
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
typedef long long LL;
const int maxn=205, maxp=20405, maxe=80205;
int n,mp[maxn][maxn],dg[maxn],sum;
int ReadDigit()
{
char ch=getchar();
while (ch<'0' || ch>'9') ch=getchar();
return ch-'0';
}
int tot,go[2*maxe],val[2*maxe],Cos[2*maxe],nxt[2*maxe],f1[maxp];
void ins(int x,int y,int z,int c)
{
go[++tot]=y;
val[tot]=z;
Cos[tot]=c;
nxt[tot]=f1[x];
f1[x]=tot;
}
LL ans;
int d[10*maxp],dis[maxp],fro[maxp][2];
bool bz[maxp];
void McMf()
{
while (1)
{
memset(dis,127,sizeof(dis)), dis[0]=0;
bz[ d[1]=0 ]=1;
for(int i=1, j=1; i<=j; i++)
{
for(int p=f1[d[i]]; p; p=nxt[p]) if (val[p] && dis[d[i]]+Cos[p]<dis[go[p]])
{
dis[go[p]]=dis[d[i]]+Cos[p];
fro[go[p]][0]=d[i], fro[go[p]][1]=p;
if (!bz[go[p]])
{
bz[ d[++j]=go[p] ]=1;
if (dis[d[i+1]]>dis[d[j]]) swap(d[i+1],d[j]);
}
}
bz[d[i]]=0;
}
if (dis[sum]==2139062143) break;
for(int i=sum; i; i=fro[i][0])
{
int p=fro[i][1];
val[p]--, val[(p&1) ?p+1 :p-1 ]++;
ans+=Cos[p];
}
}
}
int T;
int main()
{
scanf("%d",&T);
while (T--)
{
tot=0;
memset(f1,0,sizeof(f1));
scanf("%d",&n);
memset(dg,0,sizeof(dg));
fo(i,1,n)
fo(j,1,n)
{
mp[i][j]=ReadDigit();
if (mp[i][j]==1) dg[i]++;
}
fo(i,1,n)
{
ins(0,i,n+5,0), ins(i,0,0,0);
fo(j,dg[i]+1,n-1) ins(i,n+i,1,j*(j-1)/2-(j-1)*(j-2)/2), ins(n+i,i,0,0);
}
sum=2*n;
fo(i,1,n-1)
fo(j,i+1,n) if (mp[i][j]==2)
{
sum++;
ins(n+i,sum,1,0), ins(sum,n+i,0,0);
ins(n+j,sum,1,0), ins(sum,n+j,0,0);
}
fo(i,2*n+1,sum) ins(i,sum+1,1,0), ins(sum+1,i,0,0);
sum++;
ans=0;
fo(i,1,n) ans+=dg[i]*(dg[i]-1)/2;
McMf();
ans=(LL)n*(n-1)*(n-2)*(n-3)-ans*(n-3)*8;
printf("%I64d\n",ans);
}
}