• 题解-POI2014 Supercomputer


    Problem

    辣鸡bzoj权限题,洛谷链接

    题意概要:一棵 (n) 个点有根树。(Q) 次询问给出一个 (K),回答遍历完整棵树所需最少操作次数。每次操作可以选择访问不超过 (K) 个未访问的点,且这些点的父亲必须在之前被访问过。

    Solution

    一开始题意理解错了,以为同时段每个点都能扩展 (k) 次,然后就无耻地看了题解……但是有个证明网上都没写,这也正是写这篇博客的原因

    大概意思是说,最优策略一定是先用 (i) 步取完前 (i) 层,然后选取剩下深度大于 (i) 的节点时每次都能取满。规范一下就是设 (s[i]) 表示深度大于 (i) 的节点个数,则答案为 (maxlimits_{i=1}^n {i+lceil frac {s[i]}k ceil})

    然后就可以正常斜率优化了:维护点 ((i,s[i])) 的上凸包,每次询问 (i) 则在凸包上找斜率为 (-i) 的直线所切的点

    但是看题解的时候不清楚前边那个式子的正确性,为什么会存在一条分界线使得上面可以用 (i) 次选完 (i) 层,下面可以随意选

    思考了一会才弄懂,对于一棵树,从底层选择一个最深的深度 (d),显然满足下部节点可以随意选择(目前没有下部节点),但是有可能上部节点不能在层数次内选完。若出现这种情况,则上部一定存在若干层节点特别多,导致无法消完,可以考虑将分界线挪到某一层的上一层,由于这层节点特别多,可以保证下部节点可以随意选择。所以存在分界线比深度 (d) 浅,将 (d) 减小继续判断直到无法挪动,则找到分界线

    至于为什么 (maxlimits_{i=1}^n {i+lceil frac {s[i]}k ceil}) 中取最大值即可,是因为如若没有选择到分界线,则算出的值一定比最终答案要小

    Code

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    typedef double db;
    
    template <typename _tp> inline void read(_tp&x){
    	char c11=getchar();x=0;while(!isdigit(c11))c11=getchar();
    	while(isdigit(c11))x=x*10+c11-'0',c11=getchar();
    }
    
    const int N = 1001000;
    struct Edge{int v,nxt;} a[N+N];
    int head[N], s[N], Ans[N], qy[N];
    ll f[N];
    int n,Q,tp,_,mxd;
    
    void dfs(int x,int ds){
    	++s[ds], mxd = max(mxd, ds);
    	for(int i=head[x];i;i=a[i].nxt)
    		dfs(a[i].v,ds+1);
    }
    
    struct vec{
    	ll x,y;
    	inline void in(){read(x), read(y);}
    	friend inline vec operator - (const vec&A,const vec&B) {return (vec){A.x - B.x, A.y - B.y};}
    	friend inline db operator * (const vec&A,const vec&B) {return (db)A.x * B.y - (db)A.y * B.x;}
    	friend inline bool operator < (const vec&A,const vec&B) {return A.x!=B.x ? A.x<B.x : A.y>B.y;}
    }p[N],stk[N];
    
    int main(){
    	read(n), read(Q);
    	for(int i=1;i<=Q;++i) read(qy[i]);
    	for(int i=2,x;i<=n;++i) read(x), a[++_].v = i, a[_].nxt = head[x], head[x] = _;
    	dfs(1,0);
    	for(int i=n;~i;--i) s[i] += s[i+1];
    	for(int i=0;;++i){
    		vec nw = (vec) {i, s[i]};
    		while(tp > 1 and (stk[tp] - stk[tp-1]) * (nw - stk[tp]) >= 0) --tp;
    		stk[++tp] = nw;
    		if(!s[i]) break;
    	}
    	for(int i=1,t=1;i<=n;++i){
    		vec dir = (vec) {1, -i};
    		while(t < tp and (stk[t+1] - stk[t]) * dir < 0) ++t;
    		Ans[i] = (stk[t].y + (ll)i * stk[t].x + i-1) / i;
    	}
    	++mxd;
    	for(int i=1;i<=Q;++i){
    		if(Ans[qy[i]]) printf("%d ",Ans[qy[i]]);
    		else printf("%d ",mxd);
    	}
    	return 0;
    }
    
  • 相关阅读:
    Tapestry5 Setup with HTML5 and jQuery
    CSS学习六:布局剖析
    CSS学习三:表格,表单,超链接和鼠标等。
    CSS+DIV实战
    一到关于c++继承和多态的题
    一道有关指针的题
    小程序,嘿嘿
    《犯罪心理》名言部分
    ethereal与wincap
    并发编程之:Lock
  • 原文地址:https://www.cnblogs.com/penth/p/10577572.html
Copyright © 2020-2023  润新知