【题解】[JOISC 2021 Day4] 最悪の記者 4
子任务 \(1\)
树,\(n\le 5\times 10^3\) 。
考虑 \(n^2\) 动态规划。
定义 \(f[i][j]\) 表示已 \(i\) 为根,根的值为 \(j\) 的最小代价。
显然 \(j\) 只能取出现过的数,一共 \(n\) 种。
\[f[i][j]=c_i\times[j=h_i]+\sum \limits_{u\in son}\min\limits_{k\ge j}\{f[u][k]\}
\]
直接转移即可,时间复杂度 \(\mathcal{O}(n^2)\) 。
#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define pre(i,a,b) for(int i=a;i>=b;i--)
#define N 5005
using namespace std;
int n,a[N],u[N],c[N],o[N],T,b[N],h[N],tot;long long f[N][N];
struct edge{int to,nxt;}e[N<<1];
void add(int x,int y){e[++tot].nxt=h[x];h[x]=tot;e[tot].to=y;}
void dfs(int x){
int cur=lower_bound(b+1,b+T+1,u[x])-b;
rep(i,1,T)f[x][i]=c[x];f[x][cur]=0;
for(int i=h[x];i;i=e[i].nxt){
dfs(e[i].to);long long mn=0x7fffffffffffffffLL;
pre(j,T,1)mn=min(mn,f[e[i].to][j]),f[x][j]+=mn;
}
}
int main(){
scanf("%d",&n);
rep(i,1,n)scanf("%d%d%d",&a[i],&u[i],&c[i]),o[i]=u[i];
sort(o+1,o+n+1);rep(i,1,n)if(o[i]!=o[i-1])b[++T]=o[i];
rep(i,2,n)add(a[i],i);dfs(1);long long ans=0x7fffffffffffffffLL;
rep(i,1,T)ans=min(ans,f[1][i]);printf("%lld\n",ans);
return 0;
}
子任务 \(2\)
树, \(n\le 2\times 10^5\) 。
对于以 \(i\) 为根的子树,\(f[i][j]\) 中 \(j\) 的取值一定是其子树中的值。
考虑线段树合并优化 \(\rm DP\) 。
合并时先合并右子树,再合并左子树,记录 \(u\) 表示当前第一颗树中的最小值,\(v\) 表示第二颗树中的最小值。
对于线段树上每个节点,维护区间最小值,和区间加的标记。注意及时下传标记。
时间复杂度 \(\mathcal{O}(n\log n)\) 。
#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define pre(i,a,b) for(int i=a;i>=b;i--)
#define int long long
#define N 200005
using namespace std;
int n,h[N],tot,u[N],c[N],o[N],t,b[N],w[N];
struct edge{int to,nxt;}e[N];
void add(int x,int y){e[++tot].nxt=h[x];h[x]=tot;e[tot].to=y;}
struct node{
int l,r,mn,tag;
}a[N<<6];
#define ls a[x].l
#define rs a[x].r
#define S a[x].mn
#define T a[x].tag
int idx,rt[N],pm,qm;
void pushup(int x,int val){if(x)S+=val,T+=val;}
void down(int x){if(T)pushup(ls,T),pushup(rs,T),T=0;}
int ins(int x,int l,int r,int pos,int val){
if(!x){x=++idx,T=S=0;}
if(l==r){S=val;return x;}
down(x);int mid=(l+r)>>1;
if(mid>=pos)ls=ins(ls,l,mid,pos,val);
else rs=ins(rs,mid+1,r,pos,val);
S=min(a[ls].mn,a[rs].mn);return x;
}
int merge(int x,int y,int l,int r){
if(!x&&!y)return 0;
if(!x){qm=min(qm,a[y].mn);pushup(y,pm);return y;}
if(!y){pm=min(pm,a[x].mn);pushup(x,qm);return x;}
if(l==r){
pm=min(pm,a[x].mn);qm=min(qm,a[y].mn);
if(a[x].mn+qm<=a[y].mn+pm){pushup(x,qm);return x;}
pushup(y,pm);return y;
}
down(x);down(y);int mid=(l+r)>>1;
rs=merge(a[x].r,a[y].r,mid+1,r);
ls=merge(a[x].l,a[y].l,l,mid);
S=min(a[ls].mn,a[rs].mn);return x;
}
int ask(int x,int l,int r,int L,int R){
if(!x)return 0;
if(L>=l&&R<=r)return S;
down(x);
int mid=(L+R)>>1,cur=0;
if(mid>=l)cur=min(cur,ask(ls,l,r,L,mid));
if(mid<r)cur=min(cur,ask(rs,l,r,mid+1,R));
return cur;
}
void dfs(int x){
rt[x]=ins(0,1,t+1,t+1,0);
for(int i=h[x];i;i=e[i].nxt){
dfs(e[i].to);pm=qm=0;
rt[x]=merge(rt[x],rt[e[i].to],1,t+1);
w[x]+=w[e[i].to];
}
int cur=lower_bound(b+1,b+t+1,u[x])-b;
int now=ask(rt[x],cur,t+1,1,t+1);
rt[x]=ins(rt[x],1,t+1,cur,now-c[x]);w[x]+=c[x];
}
signed main(){
scanf("%lld",&n);
rep(i,1,n){
int x;scanf("%lld%lld%lld",&x,&u[i],&c[i]);
o[i]=u[i];if(i>1)add(x,i);
}
sort(o+1,o+n+1);rep(i,1,n)if(o[i]!=o[i-1])b[++t]=o[i];
dfs(1);
printf("%lld\n",a[rt[1]].mn+w[1]);
puts("No Copy");return 0;
}
子任务 \(3\)
基环内向树森林,\(n\le 2\times 10^5\) 。
先一遍拓扑将环删掉,对于剩下的树跑子任务 \(2\) 。
将环看成一个点,将它的子树合并到一颗线段树中。
最后枚举整个环的最终值,同时在线段树中区间查询最小值,更新答案。
环的最终值一定是环上某个点的值,或者是最小值 \(1\) 。
时间复杂度 \(\mathcal{O} (n\log n)\) 。
#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define pre(i,a,b) for(int i=a;i>=b;i--)
#define int long long
#define N 200005
using namespace std;
int n,h[N],tot,u[N],c[N],o[N],t,b[N],w;
struct edge{int to,nxt;}e[N];
void add(int x,int y){e[++tot].nxt=h[x];h[x]=tot;e[tot].to=y;}
struct node{
int l,r,mn,tag;
}a[N<<6];
#define ls a[x].l
#define rs a[x].r
#define S a[x].mn
#define T a[x].tag
int idx,rt[N],pm,qm,wt[N];
void pushup(int x,int val){if(x)S+=val,T+=val;}
void down(int x){if(T)pushup(ls,T),pushup(rs,T),T=0;}
int ins(int x,int l,int r,int pos,int val){
if(!x){x=++idx,T=S=0;}
if(l==r){S=val;return x;}
down(x);int mid=(l+r)>>1;
if(mid>=pos)ls=ins(ls,l,mid,pos,val);
else rs=ins(rs,mid+1,r,pos,val);
S=min(a[ls].mn,a[rs].mn);return x;
}
int change(int x,int l,int r,int pos,int val){
if(!x){x=++idx,T=S=0;}
if(l==r){S+=val;return x;}
down(x);int mid=(l+r)>>1;
if(mid>=pos)ls=ins(ls,l,mid,pos,val);
else rs=ins(rs,mid+1,r,pos,val);
S=min(a[ls].mn,a[rs].mn);return x;
}
int merge(int x,int y,int l,int r){
if(!x&&!y)return 0;
if(!x){qm=min(qm,a[y].mn);pushup(y,pm);return y;}
if(!y){pm=min(pm,a[x].mn);pushup(x,qm);return x;}
if(l==r){
pm=min(pm,a[x].mn);qm=min(qm,a[y].mn);
if(a[x].mn+qm<=a[y].mn+pm){pushup(x,qm);return x;}
pushup(y,pm);return y;
}
down(x);down(y);int mid=(l+r)>>1;
rs=merge(a[x].r,a[y].r,mid+1,r);
ls=merge(a[x].l,a[y].l,l,mid);
S=min(a[ls].mn,a[rs].mn);return x;
}
int ask(int x,int l,int r,int L,int R){
if(!x)return 0;
if(L>=l&&R<=r)return S;
down(x);
int mid=(L+R)>>1,cur=0;
if(mid>=l)cur=min(cur,ask(ls,l,r,L,mid));
if(mid<r)cur=min(cur,ask(rs,l,r,mid+1,R));
return cur;
}
void dfs(int x){
rt[x]=ins(0,1,t,t,0);
for(int i=h[x];i;i=e[i].nxt){
dfs(e[i].to);pm=qm=0;
rt[x]=merge(rt[x],rt[e[i].to],1,t);
}
int cur=lower_bound(b+1,b+t+1,u[x])-b;
int now=ask(rt[x],cur,t,1,t);
rt[x]=ins(rt[x],1,t,cur,now-c[x]);
}
queue<int>q;int in[N],cnt,dfn[N],f[N];vector<pair<int,int> >cir[N];
void mark(int x){
dfn[x]=cnt;
cir[cnt].push_back(make_pair(lower_bound(b+1,b+t+1,u[x])-b,-c[x]));
if(!dfn[f[x]])mark(f[x]);
}
void topo(){
rep(i,1,n)if(!in[i])q.push(i);
while(!q.empty()){
int x=q.front();q.pop();
in[f[x]]--;
if(!in[f[x]])q.push(f[x]);
}
rep(i,1,n)if(!dfn[i]&&in[i])++cnt,mark(i);
}
void calc(){
rep(i,1,n)if(!in[i]&&in[f[i]])dfs(i),qm=pm=0,wt[dfn[f[i]]]=merge(wt[dfn[f[i]]],rt[i],1,t);
rep(i,1,cnt){
sort(cir[i].begin(),cir[i].end());int mn=a[wt[i]].mn;
int sum=cir[i][0].second,pre=cir[i][0].first;
for(int j=1;j<(int)cir[i].size();j++){
if(cir[i][j].first==pre)sum+=cir[i][j].second;
else {
mn=min(mn,ask(wt[i],pre,t,1,t)+sum);
sum=cir[i][j].second,pre=cir[i][j].first;
}
}
w+=min(mn,ask(wt[i],pre,t,1,t)+sum);
}
}
signed main(){
scanf("%lld",&n);
rep(i,1,n){
scanf("%lld%lld%lld",&f[i],&u[i],&c[i]);
o[i]=u[i];w+=c[i];add(f[i],i);in[f[i]]++;
}
sort(o+1,o+n+1);rep(i,1,n)if(o[i]!=o[i-1])b[++t]=o[i];
puts("No Copy");topo();calc();
printf("%lld\n",w);
return 0;
}