zoukankan      html  css  js  c++  java
  • 【CF516D】Drazil and Morning Exercise

    题目

    首先我们知道,在树上距离一个点最远的点一定是直径的两个端点之一

    首先两遍( m dfs)把直径求出来,定义(d(u))表示点(u)距离其最远点的距离,有了直径我们就能求出(d)数组了

    当然可以树形( m dp),设(f_{x,i,j})表示在(x)子树内部选择一个最大值为(i)最小值为(j)的最大联通块是多少,显然这样的复杂度奇高无比

    考虑把求出的直径来好好利用一下

    首先感性的发现一下,设(x)为直径中点,那么(d(x))肯定是最小的点权,又发现我们钦定(x)为根,那么一个点的点权肯定比其子树内部的点都要小

    那么我们就钦定(x)为根,之后枚举一个联通块的最高点(i),也就是点权的最小值

    由于深度越大点权越大,于是我们就把这个子树内部所有点权不超过(d(i)+L)的点都选上,这显然构成一个联通块

    于是问题转化成了求一个子树内部有多少个点的点权小于某个给定值,不难发现这是主席树裸题

    说的好,于是我选择( m dsu on tree),复杂度(O(nqlog n+nlog^2n)),莫名其妙跑得比并查集老哥们快

    代码

    #include<bits/stdc++.h>
    #define re register
    #define LL long long
    #define lb(x) ((x)&(-x))
    #define max(a,b) ((a)>(b)?(a):(b))
    inline int read() {
    	char c=getchar();int x=0;while(c<'0'||c>'9') c=getchar();
    	while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-48,c=getchar();return x;
    }
    const int maxn=1e5+5;
    struct E{int v,nxt,w;}e[maxn<<1];
    int n,m,num,Rt,rt,sz,S;
    int head[maxn],sum[maxn],son[maxn],pos[maxn],dep[maxn],d[maxn],Ans[55];
    LL pre[maxn],mx[maxn],q[55],c[maxn];
    inline void add(int x,int y,int w) {
    	e[++num].v=y;e[num].nxt=head[x];head[x]=num;e[num].w=w;
    }
    inline int find(LL x) {
    	int l=1,r=sz,now=0;
    	while(l<=r) {
    		int mid=l+r>>1;
    		if(c[mid]<=x) now=mid,l=mid+1;else r=mid-1;
    	}
    	return now;
    }
    void dfs(int x,int fa) {
    	for(re int i=head[x];i;i=e[i].nxt) {
    		if(e[i].v==fa) continue;
    		pre[e[i].v]=pre[x]+e[i].w;
    		dfs(e[i].v,x);
    	}
    }
    void dfs1(int x) {
    	sum[x]=1;
    	for(re int i=head[x];i;i=e[i].nxt) {
    		if(dep[e[i].v]) continue;
    		dep[e[i].v]=dep[x]+1;dfs1(e[i].v);sum[x]+=sum[e[i].v];
    		if(sum[e[i].v]>sum[son[x]]) son[x]=e[i].v;
    	}
    }
    inline void add(int x,int v) {
    	for(re int i=x;i<=sz;i+=lb(i)) d[i]+=v;
    }
    inline int ask(int x) {
    	int now=0;
    	for(re int i=x;i;i-=lb(i)) now+=d[i];
    	return now;
    }
    void calc(int x,int o) {
    	add(pos[x],o);
    	for(re int i=head[x];i;i=e[i].nxt)
    	if(dep[e[i].v]>dep[x]&&S!=e[i].v) calc(e[i].v,o);
    }
    void dsu(int x,int k) {
    	for(re int i=head[x];i;i=e[i].nxt) 
    	if(dep[e[i].v]>dep[x]&&son[x]!=e[i].v) dsu(e[i].v,0);
    	if(son[x]) dsu(son[x],1);
    	S=son[x],calc(x,1),S=0;
    	for(re int i=1;i<=m;i++) {
    		int g=ask(find(mx[x]+q[i]));
    		Ans[i]=max(Ans[i],g);
    	}
    	if(!k) calc(x,-1);
    }
    int main() {
    	n=read();
    	for(re int x,y,w,i=1;i<n;++i)
    		x=read(),y=read(),w=read(),add(x,y,w),add(y,x,w);
    	m=read();for(re int i=1;i<=m;i++) scanf("%lld",&q[i]);
    	dfs(1,0);rt=1;
    	for(re int i=2;i<=n;i++) if(pre[i]>pre[rt]) rt=i;
    	pre[rt]=0;dfs(rt,0);Rt=1;
    	for(re int i=1;i<=n;i++) mx[i]=pre[i];
    	for(re int i=2;i<=n;i++) if(pre[i]>pre[Rt]) Rt=i;
    	pre[Rt]=0;dfs(Rt,0);
    	for(re int i=1;i<=n;i++) mx[i]=max(mx[i],pre[i]);
    	rt=1;for(re int i=2;i<=n;i++) if(mx[i]<mx[rt]) rt=i;
    	dep[rt]=1;dfs1(rt);
    	for(re int i=1;i<=n;i++) c[i]=mx[i];
    	std::sort(c+1,c+n+1);sz=std::unique(c+1,c+n+1)-c-1;
    	for(re int i=1;i<=n;i++) pos[i]=find(mx[i]);
    	dsu(rt,1);for(re int i=1;i<=m;i++) printf("%d
    ",Ans[i]);
    	return 0;
    }
    
    
  • 相关阅读:
    数据结构与算法20170804
    设计模式之抽象工厂模式20170803
    设计模式之建造者模式20170802
    设计模式之工厂方法模式20170801
    设计模式之中介者模式20170731
    设计模式之门面模式20170728
    设计模式之适配器模式20170727
    设计模式之装饰模式20170726
    AndroidStudio 开发JNI
    NDK开发: 打印C代码的调试信息Log
  • 原文地址:https://www.cnblogs.com/asuldb/p/11523331.html
Copyright © 2011-2022 走看看