題目大意
給定一個仙人掌,每條邊有顏色,求將原仙人掌斷邊成樹後邊權最多有多少不同的。
解題報告
仙人掌有性質為:一條邊不會在多個環內。意味著各個環的操作是獨立的。考慮統計所有環上的顏色和數量,此部分易實現。
最大化顏色種類數,等價於最小化所失去的顏色數量。
易轉化成強制無法刪除所有種類顏色,最終剩餘幾個環無法刪除即為要刪除多少的顏色。
該部分有若干限制,控制了每種顏色最多用多少,每個環只能刪除一個,觀察資料範圍不超過 \(1e4\),考慮用網路流解決問題。
令 \(S,T\) 為超源和超匯,該模型較為簡單,考慮直接建出。
-
\(S\to i\ 連一條容量為 \ all_i-1\ 的邊,\\ 表示一共只能刪除\ all_i-1\ 條顏色 \ i\ 的邊,其中 \ i\ 為顏色\ i\ ,\ all_i\ 為環上邊顏色為 \ i\ 的數量\\ (若顏色 \ i\ 有非環邊,則容量為 \ all_i\ )\)
-
\(i\to i'連一條容量為1的邊,表示每個環只能刪除環內顏色的一條邊,其中i'為第i'個環\)
-
\(i'\to T\ 連一條容量為1的邊,表示一個集合最多刪除一條邊\)
那麼最後求出的最大流即為在不刪除顏色的前提下每個環刪除一條邊的最大環數,則要刪除的顏色數即為環的總數量-最大流。
程式碼
寫的好醜
void add(int x,int y,int c){
to[++idx]=y,from[idx]=x,col[idx]=c,ne[idx]=h[x],h[x]=idx;
}
void dfs(int u,int par){
dfn[u]=++ti;
for(int i=h[u];i;i=ne[i]){
int v=to[i];
if(v==par) continue;
if(!dfn[v]){
sta[++top]=i;
dfs(v,u);
}
if(dfn[v]<dfn[u]){
++tot;
cnt[tot][col[i]]++;
vis[i]=vis[i^1]=1;
while(from[sta[top]]!=v&&top){
cnt[tot][col[sta[top]]]++;
vis[sta[top]]=1;
vis[sta[top]^1]=1;
top--;
}
vis[sta[top]]=1;
vis[sta[top]^1]=1;
cnt[tot][col[sta[top]]]++;
top--;
}
if(sta[top]==i) top--;
}
}
int all[N],S,T,col_id[N],cur[N],dis[N];
void add_edge(int u,int v,int c){
to[++idx]=v,cap[idx]=c,ne[idx]=h[u],h[u]=idx;
to[++idx]=u,cap[idx]=0,ne[idx]=h[v],h[v]=idx;
}
void bfs(){
memset(dis,-1,sizeof dis);
queue <int> q;
q.push(S),dis[S]=0;
while(q.size()){
int u=q.front();
q.pop();
for(int i=h[u];i;i=ne[i]){
int v=to[i];
if(!cap[i]) continue;
if(!~dis[v]){
dis[v]=dis[u]+1;
q.push(v);
}
}
}
}
int DFS(int u,int f){
if(u==T) return f;
int tmp=f;
for(int i=h[u];i;i=ne[i]){
if(!cap[i]||dis[to[i]]!=dis[u]+1)
continue;
int d=DFS(to[i],min(cap[i],f));
if(d){
cap[i]-=d,cap[i^1]+=d;
tmp-=d;
if(!tmp) break;
}
else dis[to[i]]=0;
}
return f-tmp;
}
int dinic(){
int res=0;
while(1){
bfs();
if(!~dis[T]) break;
res+=DFS(S,2e9);
}
return res;
}
void Main(){
// memset(h,-1,sizeof h);
n=rd,m=rd;
int col_all_cnt=0;
for(int i=1;i<=m;i++){
int u=rd,v=rd,w=rd;
if(!no[w]) no[w]=1,col_all_cnt++;
add(u,v,w),add(v,u,w);
}
dfs(1,-1);
memset(no,0,sizeof no);
for(int i=2;i<=idx;i++){
if(vis[i]) continue;
no[col[i]]=1;
}
memset(h,0,sizeof h),idx=1;
for(int i=1;i<=tot;i++){
for(auto it:cnt[i])
all[it.first]+=it.second;
}
S=0,T=1,ti=1;
for(int i=1;i<=m;i++)
if(all[i])
col_id[i]=++ti,add_edge(S,ti,all[i]-(!no[i]));
for(int i=1;i<=m;i++)
if(!col_id[i])
col_id[i]=++ti;
for(int i=1;i<=tot;i++){
++ti;
for(auto it:cnt[i])
add_edge(col_id[it.first],ti,1);
add_edge(ti,T,1);
}
cout<<col_all_cnt-(tot-dinic())<<endl;
}