题目链接:https://open.kattis.com/problems/rainbowgraph

题目大意:

一张无向图,每条边有红绿蓝三种颜色,请选一个最小权值和的边集,使得:不看红色边图是联通的,不看蓝色边图也是联通的。

做法

两个对偶拟阵一交就做完了,需要注意的是,带权拟阵交每次增广出来的集合,都是与他同大小的权值和最大的集合,所以一遍带权拟阵交就能处理出所有的答案。

记得特判图一开始是不是联通的,因为如果一开始就不联通,那么 $\emptyset\notin \mathcal{I}$ ,这与拟阵交一开始默认 $\emptyset \in \mathcal{I}$ 冲突。

时间复杂度:$O(m^4)$ 。

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
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> PII;
const int inf=1e9;
const int N=1e2+5;
const int M=2e4+5;
int n,m,zans;
struct Edge{
int x,y,c,typ;
}e[N];
bool in[N];
namespace Union{
int fa[N];
int findfa(int x){return fa[x]==x?x:fa[x]=findfa(fa[x]);}
void mer(int x,int y){
x=findfa(x);y=findfa(y);
if(x!=y)fa[x]=y;
}
bool pd(int ban,int mov,int add){
for(int i=1;i<=n;i++)fa[i]=i;
for(int i=1;i<=m;i++){
if(e[i].typ==ban || i==mov)continue;
if(i!=add && in[i])continue;
mer(e[i].x,e[i].y);
}
for(int i=2;i<=n;i++){
if(findfa(i)!=findfa(1))return 0;
}
return 1;
}
}
using Union::pd;

struct node{
int y,next;
}a[M];int len,las[N];
void ins(int x,int y){a[++len]={y,las[x]};las[x]=len;}
int pri[N],typ[N];
void build(){
len=0;
memset(las+1,0,sizeof(int)*m);
memset(typ+1,0,sizeof(int)*m);
for(int i=1;i<=m;i++){
if(in[i]){
for(int j=1;j<=m;j++){
if(in[j])continue;
if(pd(0,j,i))ins(i,j);
if(pd(2,j,i))ins(j,i);
}
}
else{
if(pd(0,i,-1))typ[i]|=1;
if(pd(2,i,-1))typ[i]|=2;
}
if(!in[i])pri[i]=e[i].c;
else pri[i]=-e[i].c;
}
}
int pre[N];PII d[N];
bool v[N];
queue<int> q;
bool solve(){
assert(q.empty());
for(int i=1;i<=m;i++)d[i]={-inf,-inf};
for(int i=1;i<=m;i++){
if(typ[i]&1){
q.push(i);
v[i]=1;
d[i]={pri[i],0};
}
}
while(!q.empty()){
int x=q.front();q.pop();
v[x]=0;
for(int k=las[x];k;k=a[k].next){
int y=a[k].y;
if(PII{d[x].first+pri[y],d[x].second-1}>d[y]){
d[y]=PII{d[x].first+pri[y],d[x].second-1};
pre[y]=x;
if(!v[y]){
v[y]=1;
q.push(y);
}
}
}
}
PII maxval={-inf,-inf};int mp=0;
for(int i=1;i<=m;i++){
if((typ[i]&2) && d[i]>maxval){
maxval=d[i];
mp=i;
}
}
if(!mp)return 0;
zans-=maxval.first;
int now=mp;
while(now){
in[now]^=1;
now=pre[now];
}
return 1;
}
char ss[10];
int ans[N];
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
int x,y,w,t;scanf("%d%d%d%s",&x,&y,&w,ss+1);
if(ss[1]=='R')t=0;
else if(ss[1]=='G')t=1;
else t=2;
e[i]={x,y,w,t};
zans+=w;
}
for(int i=1;i<=m;i++)ans[i]=-1;
if(pd(0,-1,-1) && pd(2,-1,-1)){
ans[m]=zans;
for(int i=m-1;i>=1;i--){
build();
if(!solve())break;
ans[i]=zans;
}
}
for(int i=1;i<=m;i++)printf("%d\n",ans[i]);
return 0;
}