[JOISC 2019 Day3]指定城市

指定城市

题解

这题其实挺水的。

很明显,没被选的边的边权和最小也就是求出所有被选的边的边权和最大。

对于subtask2:也就是求只有一个点为指定点的情况。也就是求出所有点到他需要建的边。我们很容易求出对于一个固定点的所有儿子的子树上的点到他的和。这个一个dfs就可以解决。那么我们是否可以通过它父亲为root时的和求出它为root时的和吗?当然可以的。我们只需要再进行一次dfs,将它本身从树上切出去,然后再连到它本身就可以了。具体来说就是:

sum_{v}= (sum_{u}-sum_{v}-paid_{u\rightarrow v})+paid_{v\rightarrow u}+ sum_{v}

这样,O\left(n \right )就可以解决问题了。

对于subtask3:即求两个点为指定点的情况。我们很容易从subtask 2中发现,对于一个点为root时的情况,当选择两个不在同一子树的叶节点时,肯定有一种情况是最大的。我们只需要维护对于一个点它子树上最深的叶节点,在维护其父亲时选择其父亲的两个儿子子树上的最深叶节点的情况即可。因为当这两个叶节点的距离最远时,在这棵树为root时一定可以得到最优的答案。

对于剩余的subtask:

我们发现我们在找到这两个指定点后,我们可以将其中一个设为根节点,就不用向上面一样维护每个节点为root值时的值那样麻烦了。我们需要知道现在加上一个指定点会发生什么,就是从这个指定点到根的未建设的边全部被建设。我们可以根据dfs序,建出一棵线段树,来维护每个点为指定点时会减去边数的情况。每次选出现在会造成最大影响的点,将它到根的边依次从线段树上除去,也就是对于每条边到达的儿子节点的子树上去掉这条边,每次维护一条边需要O\left(logn \right )的时间复杂度。但并不是每次都会有这么多,因为每条去掉的边的子树大小都是不一样的,而且每条边都是不一样的,其实时间复杂度为O\left(\sum_{i=1}^{n} dep_{i}\cdot sum_{i} \right )的样子,反正不会T。

这样就可以解决这道题了。

源码

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
#define MAXN 500005
typedef long long LL;
#define int LL
#define gc() getchar()
template
void read(_T &x){
	_T f=1;x=0;char s=gc();
	while(s>'9'||s<'0'){if(s=='-')f=-1;s=gc();}
	while(s>='0'&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=gc();}
	x*=f;
}
int n,q,ans[MAXN],p1,p2;
int to[MAXN],nxt[MAXN],paid[MAXN],head[MAXN],tott;
int tot,len[MAXN],sum[MAXN],pos[MAXN],tmp[MAXN],vis[MAXN];
int father[MAXN],dfn[MAXN],idx,id[MAXN],low[MAXN];
int mx[MAXN<<2],lzy[MAXN<<2];
void addEdge(int u,int v,int w){
	to[++tott]=v;paid[tott]=w;
	nxt[tott]=head[u];head[u]=tott;
}
void dfs1(int u,int fa){
	for(int i=head[u];i;i=nxt[i]){
		int v=to[i];if(v==fa)continue;
		len[v]=len[u]+paid[i];dfs1(v,u);
		sum[u]+=sum[v]+paid[i^1];
	}
}
void updata(int u,int v,int w){if(w>ans[2])p1=u,p2=v,ans[2]=w;}
int Max(int u,int v){return len[u]>len[v]?u:v;}
void dfs2(int u,int fa){
	ans[1]=max(ans[1],sum[u]);pos[u]=u;
	for(int i=head[u];i;i=nxt[i]){
		int v=to[i];if(v==fa)continue;
		sum[u]-=sum[v]+paid[i^1];sum[v]+=sum[u]+paid[i];
		dfs2(v,u);sum[v]-=sum[u]+paid[i];sum[u]+=sum[v]+paid[i^1];
		updata(pos[u],pos[v],sum[u]+len[pos[u]]+len[pos[v]]-len[u]*2);
		pos[u]=Max(pos[u],pos[v]);
	}
}
void modify(int rt,int l,int r,int al,int ar,int aw){
	if(al<=l&&r<=ar){mx[rt]+=aw;lzy[rt]+=aw;return ;}
	int mid=l+r>>1;
	if(al<=mid)modify(rt<<1,l,mid,al,ar,aw);
	if(ar>mid)modify(rt<<1|1,mid+1,r,al,ar,aw);
	mx[rt]=max(mx[rt<<1],mx[rt<<1|1])+lzy[rt];
}
void dfs3(int u,int fa){
	father[u]=fa;id[dfn[u]=++idx]=u;
	for(int i=head[u];i;i=nxt[i]){
		int v=to[i];if(v==fa)continue;
		tmp[v]=paid[i];dfs3(v,u);
	}
	low[u]=idx;modify(1,1,n,dfn[u],low[u],tmp[u]);
}
int query(int rt,int l,int r){
	if(l==r)return id[l];int mid=l+r>>1;
	if(mx[rt]==mx[rt<<1]+lzy[rt])
		return query(rt<<1,l,mid);
	return query(rt<<1|1,mid+1,r);
}
void insert(int u){while(u&&!vis[u])modify(1,1,n,dfn[u],low[u],-tmp[u]),vis[u]=1,u=father[u];}
signed main(){
	read(n);tott=1;
	for(int i=1;i

谢谢!!!

你可能感兴趣的:(#,线段树,#,树)