BZOJ4401:块的计数(乱搞)

Description

小Y最近从同学那里听说了一个十分牛B的高级数据结构——块状树。听说这种数据结构能在sqrt(N)的时间内维护树上的各种信息,十分的高效。当然,无聊的小Y对这种事情毫无兴趣,只是对把树分块这个操作感到十分好奇。他想,假如能把一棵树分成几块,使得每个块中的点数都相同该有多优美啊!小Y很想知道,能有几种分割方法使得一棵树变得优美。小Y每次会画出一棵树,但由于手速太快,有时候小Y画出来的树会异常地庞大,令小Y感到十分的苦恼。但是小Y实在是太想知道答案了,于是他找到了你,一个天才的程序员,来帮助他完成这件事。

Input

第一行一个正整数N,表示这棵树的结点总数,接下来N-1行,每行两个数字X,Y表示编号为X的结点与编号为Y的结点相连。结点编号的范围为1-N且编号两两不同。

Output

一行一个整数Ans,表示所求的方案数。

Sample Input

6
1 2
2 3
2 4
4 5
5 6

Sample Output

3

HINT

100%的数据满足N<=1000000。

Solution

我竟然天真的以为$2e8$能跑过去直到我被最大的点卡到$10s$……

感觉也说不上什么算法,就算他是乱搞吧。

一开始洲哥给了一个$n\sqrt{n}$的写法。下一段根号做法可以不看因为我感觉我写的可能比正解还难懂……

我们先枚举当前要分的块大小$k$,再随便找一个根$DFS$一下,从下往上贪心的分,也就是够$k$个就分成一块。感性理解一下还是非常正确的……对于每个点$x$我们求出$(\sum size[son[x]]\%k)+1$,如果存在某个点的这个值大于$k$的话显然就是不合法的。其中$size[son[x]]\%k$也就是$son[x]$这颗子树里面分完了剩下的点。这个为什么是对的我就不多说了……要真想不懂的话可以直接去看下面正解做法。

其实仔细想想,确定了块大小$k$之后,那么每一块内的根的$size$肯定就是$k$的倍数。这个应该还是比较显然的,因为你是从下往上贪心来分的。那么我们直接开个桶记下$size$,对于每一个能被$n$整除的块大小$k$,统计有多少$size[x]$被$k$整除,如果与$n/k$相同则合法。复杂度应该是$O(n+\sqrt{n}logn)$

Code

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cmath>
 4 #define N (1000009)
 5 using namespace std;
 6 
 7 struct Edge{int to,next;}edge[N<<1];
 8 int n,ans,size[N],u,v,Keg[N];
 9 int head[N],num_edge;
10 
11 inline int read()
12 {
13     int x=0; char c=getchar();
14     while (c<'0' || c>'9') c=getchar();
15     while (c>='0' && c<='9') x=x*10+c-'0', c=getchar();
16     return x;
17 }
18 
19 void add(int u,int v)
20 {
21     edge[++num_edge].to=v;
22     edge[num_edge].next=head[u];
23     head[u]=num_edge;
24 }
25 
26 void DFS(int x,int fa)
27 {
28     size[x]=1;
29     for (int i=head[x]; i; i=edge[i].next)
30         if (edge[i].to!=fa)
31         {
32             DFS(edge[i].to,x);
33             size[x]+=size[edge[i].to];
34         }
35     Keg[size[x]]++;
36 }
37 
38 int main()
39 {
40     n=read();
41     for (int i=1; i<=n-1; ++i)
42     {
43         u=read(); v=read();
44         add(u,v); add(v,u);
45     }
46     DFS(1,0);
47     for (int i=1; i<=n; ++i)
48         if (n%i==0)
49         {
50             int sum=0;
51             for (int j=1; i*j<=n; ++j)
52                 sum+=Keg[i*j];
53             ans+=(sum==n/i);
54         }
55     printf("%d\n",ans);
56 }

转载于:https://www.cnblogs.com/refun/p/10219614.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值