Loading

【题解】[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;
}
posted @ 2021-10-04 17:12  7KByte  阅读(63)  评论(0编辑  收藏  举报