流水账的代码,流水账的题解
RedLycoris · · 题解
题目意思很清楚不解释。
我们一共有
稍微思考一下即可发现,这个题目等价于保留代价最大的那些链,然后删掉剩余的边进行缝缝补补。(因为最后是要变为一个环,换上每个点的入读和出度均为
回头看看这个图。
对于一个树点,由于要删掉大部分边使得它的入度变为
由于这张图可能是个基环树森林,我们需要把所有的环也拼接起来,所以说,每个环上至少要断开一个点。
对于一个环,我们令
统计
如果这个环的
如果这个环的
ps. 有一个特例,整张图只有一个大环,且这个大环包括了所有点的时候,它既不需要和其他环拼接,也不需要让其他树点挤进来,所以答案就是
Code:
#include<bits/stdc++.h>
#define ll long long
#define int ll
using namespace std;
const int mxn=2e5+5;
vector<int>g[mxn];
vector<pair<int,int> >tos[mxn];
int n,a[mxn],c[mxn];
int used[mxn],cused;
int ord[mxn],cord;
int inring[mxn];
int sum[mxn],ma[mxn];
vector<vector<int> >rings;
inline void dfs(int x){
if(used[x]==cused){//find a ring
ord[++cord]=x;
vector<int>ring;
for(int i=cord-1;i;--i){
inring[ord[i]]=1;
ring.push_back(ord[i]);
if(ord[i]==x)break;
}
rings.push_back(ring);
return;
}
if(used[x])return;
ord[++cord]=x;
used[x]=cused;
dfs(a[x]);
--cord;
}
signed main(){
ios_base::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
cin>>n;
for(int i=1;i<=n;++i){
cin>>a[i]>>c[i];
g[i].push_back(a[i]);
tos[a[i]].push_back({i,c[i]});
}
for(int i=1;i<=n;++i){
if(!used[i]){
++cused;
cord=0;
dfs(i);
}
}
if(rings.size()==1 and rings[0].size()==n){
cout<<0<<endl;
return 0;
}
ll ans=0;
for(int i=1;i<=n;++i){
if(!inring[i]){
int mx=0,sm=0;
for(auto p:tos[i]){
mx=max(mx,p.second);
sm+=p.second;
}
ans+=sm-mx;
}
}
for(vector<int>ring:rings){
ll tot=0;
int cnt=0,tval,delta=0;
for(int i:ring){
int mx=0,sm=0;
for(auto p:tos[i]){
if(!inring[p.first]){
mx=max(mx,p.second);
sm+=p.second;
}else tval=c[p.first];
}
ma[i]=mx;
sum[i]=sm;
tot+=sum[i];
if(tval<mx)++cnt,delta+=mx-tval;
}
if(cnt==0){
ll res=11451491919810;
for(int i:ring){
int j=a[i];
res=min(res,tot-ma[j]+c[i]);
}
ans+=res;
}else ans+=tot-delta;
}
cout<<ans<<endl;
}