T1 ZYB和售货机
容易发现把每个物品都买成$1$是没有影响的。
然后考虑最后一个物品的方案,如果从$f_i$向$i$连边,发现每个点有一个出度多个入度,可以先默认每个物品都能买且最大获利,这样可以建出每个点出度入度都是$1$的图。
把所有边都连上是一个基环树,所以建出的若干个联通图中只有一个环。而我们要做的工作就是用最小代价把这个环断掉,形成的树上所有边都可以对答案贡献。
记每个物品的最大获利和次大获利,在图上$DFS$,每到一个点先加上最大获利,记录路径上最大获利与次大获利差的最小值,如果当前这一块是环,把答案减去这个最小值即可。
$code:$
1 #include<bits/stdc++.h> 2 #define int long long 3 using namespace std; 4 5 namespace IO{ 6 inline int read(){ 7 char ch=getchar(); int x=0,f=1; 8 while(ch<'0'||ch>'9'){ if(ch=='-') f=-1; ch=getchar(); } 9 while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } 10 return x*f; 11 } 12 inline void write(int x,char sp){ 13 char ch[20]; int len=0; 14 if(x<0){ putchar('-'); x=~x+1; } 15 do{ ch[len++]=x%10+(1<<4)+(1<<5); x/=10; }while(x); 16 for(int i=len-1;~i;--i) putchar(ch[i]); putchar(sp); 17 } 18 inline int max(int x,int y){ return x<y?y:x; } 19 inline int min(int x,int y){ return x<y?x:y; } 20 inline void swap(int &x,int &y){ x^=y^=x^=y; } 21 inline void chmax(int &x,int y){ x=x<y?y:x; } 22 inline void chmin(int &x,int y){ x=x<y?x:y; } 23 } using namespace IO; 24 25 const int NN=1e6+5; 26 int n,st,ans,cnt,mn,bel[NN],f[NN],c[NN],d[NN],a[NN],mx[NN],sc[NN],val[NN]; 27 bool vis[NN]; 28 29 void dfs(int x){ 30 if(bel[x]==cnt) return ans-=mn,void(); 31 if(bel[x]) return; 32 if(!mx[x]) return; 33 bel[x]=cnt; 34 ans+=val[mx[x]]*a[x]; 35 chmin(mn,val[mx[x]]-val[sc[x]]); 36 if(mx[x]!=x)dfs(mx[x]); 37 } 38 signed main(){ 39 freopen("goods.in","r",stdin); 40 freopen("goods.out","w",stdout); 41 n=read(); 42 for(int i=1;i<=n;i++) 43 f[i]=read(), c[i]=read(), d[i]=read(), a[i]=read(); 44 for(int i=1;i<=n;i++){ 45 val[i]=d[f[i]]-c[i]; 46 if(val[i]>val[mx[f[i]]]) sc[f[i]]=mx[f[i]], mx[f[i]]=i; 47 else if(val[i]>val[sc[f[i]]]) sc[f[i]]=i; 48 } 49 for(int i=1;i<=n;i++) 50 if(!bel[i]) ++cnt,mn=INT_MAX,dfs(i); 51 write(ans,'\n'); 52 return 0; 53 }T1