zoukankan      html  css  js  c++  java
  • LOJ

    ( ext{Bi Bi Time!})

    那是一天之前,亲爱的教练为我们拉了一套提高组难度题目,无人上 (100 ext{pts})

    ( ext{Solution})

    首先有个结论:被钦定节点的所有子树大小不超过 $lfloor frac{n}{2} floor $。

    这个结论来源一个寻找最优解的思路:假设 (v,u) 之间有一条边,我们可以把以 (u) 为根节点的树除去 (v) 这棵子树的一团子树称为左子树((x)),把以 (v) 为根节点的树除去 (u) 这棵子树的一团子树称为右子树((y))。

    那么假设我们求出 (u) 的距离和是 (dis)(v) 的距离和就是 (dis+(size_x+1)-(size_y+1)=dis+size_x-size_y)。那么如果现在有 (size_x<size_y)(v) 是会比 (u) 更优的,一直这样走下去,发现当 (size_x=size_y) 时再走就会更劣。此时的 (u,v) 就是最优解(当然这是偶数的情况,奇数就只有一个是最优解)。

    所以结论得证。

    那问题就转化成切多少刀可以使此点的每个子树大小不超过 $lfloor frac{n}{2} floor $。好吧到这一步我还是不会做

    贪心地想,我们砍边的花费都是一样的,每次肯定尽量砍 (size) 最大的子树(至于子树的大小限制我们就直接怼给子树自己来做就行了)。

    定义 (f[u]) 为使 (u) 为根的子树的大小不超过 $lfloor frac{n}{2} floor $ 的最小砍次数,(tot[u]) 为砍完之后还剩多少个点。

    注意这里子树 (f[u_{son}]) 的最优解一定会贡献给 (f[u]),因为就算把整棵子树 (u_{son}) 砍下来,如果不能维持其大小不超过 $lfloor frac{n}{2} floor $ 也是白搞。

    我们先只管子树的情况做一遍 ( ext{DFS}),关于上面父亲那一坨的情况就是换根 ( ext{DP})

    第一遍 ( ext{DFS}) 能得到根节点的答案,后面的 ( ext{DP}) 就是需要维护父亲那一坨大小不超过 $lfloor frac{n}{2} floor $,把父亲的 (tot[fa_{son}]) 和父亲的父亲那一坨剩下的 (tot) 排个序,依次选取即可,不过因为对于父亲的每个儿子都要排除自己的 (tot),所以像第一遍 ( ext{DFS}) 的直接线性选取不行,做个前缀和二分即可。

    总时间复杂度 (mathcal O(n log n))

    ( ext{Code})

    #include <cstdio>
    
    #define rep(i,_l,_r) for(register signed i=(_l),_end=(_r);i<=_end;++i)
    #define fep(i,_l,_r) for(register signed i=(_l),_end=(_r);i>=_end;--i)
    #define erep(i,u) for(signed i=head[u],v=to[i];i;i=nxt[i],v=to[i])
    #define efep(i,u) for(signed i=Head[u],v=to[i];i;i=nxt[i],v=to[i])
    #define print(x,y) write(x),putchar(y)
    
    template <class T> inline T read(const T sample) {
        T x=0; int f=1; char s;
        while((s=getchar())>'9'||s<'0') if(s=='-') f=-1;
        while(s>='0'&&s<='9') x=(x<<1)+(x<<3)+(s^48),s=getchar();
        return x*f;
    }
    template <class T> inline void write(const T x) {
        if(x<0) return (void) (putchar('-'),write(-x));
        if(x>9) write(x/10);
        putchar(x%10^48);
    }
    template <class T> inline T Max(const T x,const T y) {if(x>y) return x; return y;}
    template <class T> inline T Min(const T x,const T y) {if(x<y) return x; return y;}
    template <class T> inline T fab(const T x) {return x>0?x:-x;}
    template <class T> inline T gcd(const T x,const T y) {return y?gcd(y,x%y):x;}
    template <class T> inline T lcm(const T x,const T y) {return x/gcd(x,y)*y;}
    template <class T> inline T Swap(T &x,T &y) {x^=y^=x^=y;}
    
    #include <algorithm>
    using namespace std;
    
    const int maxn=1e6+5;
    
    int n,head[maxn],nxt[maxn<<1],to[maxn<<1],cnt,f[maxn],tot[maxn],lim,ans[maxn],pre[maxn],fa_f[maxn],fa_tot[maxn];
    struct node {
    	int x,y;
    } p[maxn];
    
    void addEdge(int u,int v) {
    	nxt[++cnt]=head[u],to[cnt]=v,head[u]=cnt;
    	nxt[++cnt]=head[v],to[cnt]=u,head[v]=cnt;
    }
    
    bool cmp(node a,node b) {
    	return a.x<b.x;
    }
    
    void dfs1(int u,int fa) {
    	tot[u]=1;
    	erep(i,u) {
    		if(v==fa) continue;
    		dfs1(v,u);
    		f[u]+=f[v],tot[u]+=tot[v];
    	}
    	if(tot[u]<=lim) return;
    	int Cnt=0;
    	erep(i,u) if(v^fa) p[++Cnt].x=tot[v];
    	sort(p+1,p+Cnt+1,cmp);
    	while(tot[u]>lim) tot[u]-=p[Cnt--].x,++f[u];
    }
    
    void dfs2(int u,int fa) {
    	ans[u]=fa_f[u]; int Cnt=0,l,r,mid,ToT,F,res;
    	erep(i,u) if(v^fa) ans[u]+=f[v],p[++Cnt]=(node){tot[v],v};
    	p[++Cnt]=(node){fa_tot[u],fa};
    	sort(p+1,p+Cnt+1,cmp);
    	rep(i,1,Cnt) pre[i]=pre[i-1]+p[i].x;
    	rep(i,1,Cnt) {
    		if(p[i].y==fa) continue;
    		l=1,r=Cnt;
    		while(l<=r) {
    			mid=l+r>>1;
    			ToT=pre[mid]+1,F=mid; // 枚举 mid 个可以剩下,其中 +1 是 u(因为对于 u 的儿子,把 (u,v) 砍了显然没有任何意义)
    			if(i<=mid) ToT-=p[i].x,--F;
    			if(ToT<=lim) res=mid,l=mid+1;
    			else r=mid-1; 
    		}
    		ToT=pre[res]+1,F=res;
    		if(i<=res) ToT-=p[i].x,--F;
    		fa_tot[p[i].y]=ToT,fa_f[p[i].y]=ans[u]-f[p[i].y]+Cnt-F-1;
    	}
    	erep(i,u) if(v^fa) dfs2(v,u);
    }
    
    int main() {
    	int u,v;
    	n=read(9); lim=n>>1;
    	rep(i,1,n-1) {
    		u=read(9),v=read(9);
    		addEdge(u,v);
    	}
    	dfs1(1,0); dfs2(1,0);
    	rep(i,1,n) print(ans[i],'
    ');
    	return 0;
    }
    
  • 相关阅读:
    ARM与MIPS平台优劣对比分析
    ARM11Linux2.6ButtonDriverBaseinfo1
    程序员都应该阅读的十一本名书
    驱动设计ARM(6410)按键驱动0基础知识点
    创业编程七个错误认识
    ARM11Linux2.6ButtonDriverBaseinfo
    Arm设计思想与高效C语言编程联系
    个人软件已死?
    评价一个软件的3个角度
    我对北理FTP联盟的建议
  • 原文地址:https://www.cnblogs.com/AWhiteWall/p/14053111.html
Copyright © 2011-2022 走看看