[POI2014]Supercomputer

题目大意:
  给定一个$n(nle10^6)$个结点的有根树,从根结点开始染色。每次可以染和已染色结点相邻的任意$k$个结点。$q(qle10^6)$组询问,每次给定$k$,问至少需要染几次?

思路:
  显然$ans=maxleft{i+leftlceilfrac{sum[i]}k ight ceil ight}$。而这样做是$O(nq)$的,观察到原式$=leftlceilfrac{maxleft{ki+sum[i] ight}}k ight ceil$。$max$中是关于$k$的一次函数,考虑使用斜率优化,做到$O(n+q)$。

 1 #include<cmath>
 2 #include<cstdio>
 3 #include<cctype>
 4 #include<algorithm>
 5 inline int getint() {
 6     register char ch;
 7     while(!isdigit(ch=getchar()));
 8     register int x=ch^'0';
 9     while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0');
10     return x;
11 }
12 const int N=1e6+1;
13 int k[N],sum[N],h[N],head=1,tail,sz,max,ans[N],q[N];
14 struct Edge {
15     int to,next;
16 };
17 Edge e[N<<1];
18 inline void add_edge(const int &u,const int &v) {
19     e[++sz]=(Edge){v,h[u]};h[u]=sz;
20 }
21 void dfs(const int &x,const int &dep) {
22     sum[dep]++;
23     max=std::max(max,dep);
24     for(int i=h[x];i;i=e[i].next) {
25         const int &y=e[i].to;
26         dfs(y,dep+1);
27     }
28 }
29 inline double calc (const int &i,const int &j) {
30     return (double)(sum[j]-sum[i])/(i-j);
31 }
32 int main() {
33     const int n=getint();
34     for(register int i=0;i<=k[0];i++) k[i]=getint();
35     for(register int i=2;i<=n;i++) {
36         add_edge(getint(),i);
37     }
38     dfs(1,0);
39     for(register int i=max;~i;i--) sum[i]+=sum[i+1];
40     for(register int i=max;~i;i--) {
41         while(head<tail&&calc(q[tail-1],q[tail])<=calc(q[tail],i)) tail--;
42         q[++tail]=i;
43     }
44     for(register int i=n;i;i--) {
45         while(head<tail&&calc(q[head],q[head+1])>=i) head++;
46         ans[i]=q[head]+ceil((double)sum[q[head]]/i);
47     }
48     for(register int i=1;i<=k[0];i++) {
49         printf("%d%c",k[i]<=n?ans[k[i]]:max+1," 
"[i==k[0]]);
50     }
51     return 0;
52 }
原文地址:https://www.cnblogs.com/skylee03/p/8662768.html