• CF516D Drazil and Morning Exercise


    题面:https://www.luogu.com.cn/problem/CF516D
    题意:给定一棵(n)个点的树,边有边权。
    定义(f_x) = (max_{i=1}^n) ( ext{dist}(x,i))
    (q)次询问,每次给出一个值(l),询问树上满足 (max_{x})f[x]
    -(min_{x}) (f[x]) (<=l),(x) (in) (s)的连通块(s)的最大大小。
    (n) (leq) 1e5,(q) (leq) 50.
    题解:
    发现(f[x])可以(O(n))求出来。
    因此我们可以将所有点的(f)求出来排个序,双指针LCT即可。
    时间复杂度(O(qnlogn))
    但这么做不简洁。我们可以找到当前(f[x])最小的节点,令它为树根,
    然后从大到小做双指针。发现这么做只会删除叶子结点,因此用带权并查集维护就好了。
    具体原因请参考树的直径的相关引理。
    注意排序时第一关键字为(f[x]),第二维为(dep[x])
    时间复杂度:(O(nlogn+qn) (alpha)(n)())
    代码:

    #include<bits/stdc++.h>
    using namespace std;
    #define re register ll
    #define F(x,y,z) for(re x=y;x<=z;x++)
    #define FOR(x,y,z) for(re x=y;x>=z;x--)
    typedef long long ll;
    #define I inline void
    #define IN inline ll
    #define C(x,y) memset(x,y,sizeof(x))
    #define STS system("pause")
    template<class D>I read(D &res){
    	res=0;register D g=1;register char ch=getchar();
    	while(!isdigit(ch)){
    		if(ch=='-')g=-1;
    		ch=getchar();
    	}
    	while(isdigit(ch)){
    		res=(res<<3)+(res<<1)+(ch^48);
    		ch=getchar();
    	}
    	res*=g;
    }
    const ll INF=1e18+7;
    struct E{
    	ll to,nt,w;
    }e[202000];
    #define T e[k].to
    ll n,m,root,head[101000],f[101000][2],dep[101000],up[101000],ma[101000],siz[101000],ans,tot=-1,X,Y,W;
    ll p[101000],v[101000];
    I add(ll x,ll y){
    	e[++tot].to=y;
    	e[tot].nt=head[x];
    	head[x]=tot;
    	e[tot].w=W;
    }
    I D_1(ll x,ll fa){
    	for(re k=head[x];k!=-1;k=e[k].nt){
    		if(T==fa)continue;
    		D_1(T,x);re val=f[T][0]+e[k].w;
    		if(val>f[x][0])f[x][1]=f[x][0],f[x][0]=val;
    		else if(val>f[x][1])f[x][1]=val;
    	}
    }
    I D_2(ll x,ll fa,ll dis){
    	if(dis>=f[x][0])f[x][1]=f[x][0],f[x][0]=dis;
    	else if(dis>f[x][1])f[x][1]=dis;
    	for(re k=head[x];k!=-1;k=e[k].nt){
    		if(T==fa)continue;
    		D_2(T,x,(f[x][0]==f[T][0]+e[k].w?f[x][1]:f[x][0])+e[k].w);
    	}
    	if(f[x][0]<W)W=f[x][0],root=x;
    }
    I D_3(ll x,ll fa,ll depth){
    	up[x]=fa;dep[x]=depth;
    	for(re k=head[x];k!=-1;k=e[k].nt){
    		if(T==fa)continue;
    		D_3(T,x,depth+1);
    	}
    }
    inline bool bbb(ll x,ll y){
    	return f[x][0]^f[y][0]?f[x][0]>f[y][0]:dep[x]>dep[y];
    }
    IN Max(ll x,ll y){return x>y?x:y;}
    IN find(ll x){
    	return ma[x]==x?x:ma[x]=find(ma[x]);
    }
    I join(int x,int y){
    	x=find(x);y=find(y);
    	ma[x]=y;siz[y]+=siz[x];
    }
    I add(ll x){
    	for(re k=head[x];k!=-1;k=e[k].nt){
    		if(T==up[x])continue;
    		join(T,x);
    	}
    	//cout<<"!"<<x<<" "<<siz[x]<<endl;
    	ans=max(ans,siz[x]);
    }
    I delet(ll x){siz[find(x)]--;}
    int main(){
    	read(n);tot=-1;C(head,-1);
    	F(i,1,n-1){
    		read(X);read(Y);read(W);add(X,Y);add(Y,X);
    	}
    	D_1(1,0);W=INF;D_2(1,0,0);D_3(root,0,1);
    	F(i,1,n)p[i]=i;sort(p+1,p+1+n,bbb);F(i,1,n)v[i]=f[p[i]][0];//,cout<<p[i]<<" "<<v[i]<<" ";
    	//cout<<endl;
    	read(m);
    	while(m--){
    		read(W);ans=1;
    		F(i,1,n)ma[i]=i,siz[i]=1;
    		re r=1;
    		F(i,1,n){
    			while(r<=n&&v[i]-v[r]<=W)add(p[r]),r++;
    			delet(p[i]);if(r>n)break;
    		}
    		printf("%lld
    ",ans);
    	}
    	return 0;
    }
    
  • 相关阅读:
    17. 偏函数
    16. 装饰器
    vim详解
    linux用户管理sudo 磁盘分区
    linux用户管理
    linux文件与目录(四)
    linux特殊权限
    linux文件和目录(二)
    linux文件和目录
    配置网络
  • 原文地址:https://www.cnblogs.com/Purple-wzy/p/12100342.html
Copyright © 2020-2023  润新知