Bzoj4435 [CERC2015]Juice Junctions

Solution

在最小割的方向上想了一个小时

显然 $i$ 到 $j$ 的最小割只有 $0,1,2,3$ 三种情况,考虑每种答案的条件。

  • $w(i,j)=0$ 时,$i$ 和 $j$ 不连通。
  • $w(i,j)=1$ 时,$i$ 和 $j$ 不在一个边双内。
  • $w(i,j)\ge 2$ 时,$i$ 和 $j$ 在一个边双内。
    • 考虑 $w(i,j)=3$ 时,一定是整张图删除任意一条边都在同一个边双中。预处理出删除每一条边时,每个点在哪个边双中。对于判断两个集合是否相同,使用哈希判断。
    • 否则 $w(i,j)=2$

如果时间充裕,请不要使用单模哈希

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define maxn 5005
#define put() putchar('\n')
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
using namespace std;
inline void read(int &x){
int f=1;x=0;char c=getchar();
while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
while (c>='0'&&c<='9') {x=x*10+c-'0';c=getchar();}
x*=f;
}
namespace Debug{
Tp void _debug(char* f,Ty t){cerr<<f<<'='<<t<<endl;}
Ts void _debug(char* f,Ty x,Ar... y){while(*f!=',') cerr<<*f++;cerr<<'='<<x<<",";_debug(f+1,y...);}
Tp ostream& operator<<(ostream& os,vector<Ty>& V){os<<"[";for(auto& vv:V) os<<vv<<",";os<<"]";return os;}
#define gdb(...) _debug((char*)#__VA_ARGS__,__VA_ARGS__)
}using namespace Debug;
int n,m,h[maxn],head=1;
int dfn[maxn],low[maxn],stac[maxn],tot,times;
struct yyy{
int to,z,flag;
inline void add(int x,int y) {
to=y;z=h[x];h[x]=head;
}
}a[maxn*3];
ull has1[maxn],has2[maxn];
const ull base1=131,base2=171,mod=998244353;
int ecc[maxn],eccnum;
inline void tarjan(int x,int pre) {
int i;
low[x]=dfn[x]=++times;stac[++tot]=x;
for (i=h[x];i;i=a[i].z) if (a[i].flag!=1&&i!=pre&&i!=(pre^1)) {
if (!dfn[a[i].to]){
tarjan(a[i].to,i);
low[x]=min(low[x],low[a[i].to]);
}
else low[x]=min(dfn[a[i].to],low[x]);

}
if (low[x]==dfn[x]) {
++eccnum;
while (stac[tot+1]!=x) {
ecc[stac[tot]]=eccnum;
tot--;
}
}
}
int fa[maxn],ans;
inline int getfa(int x) {return x==fa[x]?x:fa[x]=getfa(fa[x]);}
signed main(void){
int i,j,x,y;
read(n);read(m);
for (i=1;i<=n;i++) fa[i]=i;
for (i=1;i<=m;i++) {
read(x);read(y);
a[++head].add(x,y);
a[++head].add(y,x);
assert(x!=y);
x=getfa(x),y=getfa(y);if (x^y) fa[x]=y;
}
for (i=1;i<=m+1;i++) {
a[2*i].flag=a[2*i+1].flag=1;
times=eccnum=0;
for (j=1;j<=n;j++) dfn[j]=low[j]=ecc[j]=stac[j]=0;
for (j=1;j<=n;j++) if (!dfn[j]) tarjan(j,0);
for (j=1;j<=n;j++) has1[j]=(1ull*has1[j]*base1+(ecc[j]+1)),has2[j]=(has2[j]*base2+ecc[j]+1)%mod;
a[2*i].flag=a[2*i+1].flag=0;
}
for (i=1;i<=n;i++) {
for (j=1;j<i;j++) {
int now=ans;
if (getfa(i)!=getfa(j)) ;
else {
ans++;
if (ecc[i]==ecc[j]) {
ans++;
if (has1[i]==has1[j]&&has2[i]==has2[j]) ans++;
}
}
}
}
printf("%lld",ans);
return 0;
}

记得把栈里的数也一同清空了(对于我这种写法)。