【題解】[JOISC 2021 Day4] 最悪の記者 4
阿新 • • 發佈:2021-10-04
子任務 \(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; }