题意:
给出两个序列a和b,一次操作你可以将ans+=a[i],a[b[i]]+=a[i]。询问最后ans的最大值。
题解:
拓扑排序,遇到负权值的节点就跳过,减少它给答案带来的贡献。
code:
#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e5 + 5;
long long a[maxn], ans;
int b[maxn], c[maxn], d[maxn];
bool e[maxn];
vector<int> v;
void init(int n){
v.clear();
for(int i = 1; i <= n; i++) {
c[i] = 0;
e[i] = false;
}
}
int main(){
int n;
scanf("%d", &n);
init(n);
int x;
for(int i = 1; i <= n; i++){
scanf("%d", &x);
a[i] = (long long)x;
}
for(int i = 1; i <= n; i++) {
scanf("%d", &b[i]);
if(b[i] != -1) c[b[i]]++;
}
int k = 0;
for(int i = 1; i <= n; i++) {
if(c[i] == 0) d[++k] = i;
}
ans = 0;
for(int i = 1; i <= n; i++){
ans += a[d[i]];
if(b[d[i]] != -1){
if(a[d[i]] > 0){
a[b[d[i]]] += a[d[i]];
v.push_back(d[i]);
e[d[i]] = true;
}
c[b[d[i]]]--;
if(c[b[d[i]]] == 0) d[++k] = b[d[i]];
}
}
for(int i = n; i >= 1; i--) if(!e[d[i]]) v.push_back(d[i]);
printf("%lld\n", ans);
for(int i = 0; i < v.size(); i++) printf("%d ", v[i]);
printf("\n");
}
dalao的代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+100;
typedef long long ll;
ll a[maxn];
int b[maxn];
int inDegree[maxn];
int n;
vector<int> g[maxn];
ll ans;
void topo () {
queue<int> q;
for (int i=1;i<=n;i++)
if (inDegree[i]==0) {
q.push(i);
}
while (!q.empty()) {
int u=q.front();
q.pop();
ans+=a[u];
if (b[u]==-1) continue;
if (a[u]>0) {
g[u].push_back(b[u]);
a[b[u]]+=a[u];
}
else {
g[b[u]].push_back(u);
}
if (--inDegree[b[u]]==0) q.push(b[u]);
}
}
int main () {
scanf("%d",&n);
for (int i=1;i<=n;i++) scanf("%lld",a+i);
for (int i=1;i<=n;i++) {
scanf("%d",b+i);
if (b[i]>0) {
inDegree[b[i]]++;
}
}
topo();
printf("%lld\n",ans);
vector<int> wjm;
for (int i=1;i<=n;i++)
for (int v:g[i]) inDegree[v]++;
for (int i=1;i<=n;i++) if (inDegree[i]==0) wjm.push_back(i);
for (int i=0;i<n;i++) {
printf("%d ",wjm[i]);
for (int v:g[wjm[i]])
if (--inDegree[v]==0) wjm.push_back(v);
}
}