BZOJ 3004 吊灯 树形DP

题目大意:给定一棵树,要求将这棵树分成nk个连通块,每块大小为k,求所有可行的k

首先k一定是n的约数。(废话

然后我们有一个结论:某个k满足条件当且仅当存在nk个节点满足以每个节点为根的子树大小都是k的倍数

证明:

首先不可能存在超过nk个节点满足以每个节点为根的子树大小都是k的倍数,这是废话

首先证明必要性:

假设我们已经有了一组合法的方案,那么对于每一个连通块,我们找到这个连通块中深度最小的节点,以这个节点为根的子树大小一定是k的倍数

由于这样的节点有nk个,因此必要性得证

下面来证明充分性:

假设现在我们已经找到了nk个节点满足以每个节点为根的子树大小都是k的倍数,那么:

首先我们从根节点出发开始DFS,每遇到一个节点满足以这个节点为根的子树大小是k的倍数,就把这个节点为根的子树砍掉

这样做之后,我们得到了一个连通块和一些子树,其中连通块的大小为k的倍数,且除根节点外其余nk?1个节点都在那些子树中

对每个子树重复这一操作,一定能得到一组合法的方案

因此充分性得证

然后就好办了,我们搞出每个节点的size(这里不要DFS,会T掉),然后令fi表示大小为i的子树个数,对于每个约数在f数组中扫一遍即可

时间复杂度O(10?σ(n))=O(10?nloglogn)

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define M 1201001
using namespace std;
int n;
int divisors[2020],tot;
int fa[M],size[M],f[M];
void Initialize()
{
    memset(size,0,sizeof size);
    memset(f,0,sizeof f);
}
void Decomposition(int n)
{
    int i;
    for(i=1;i*i<n;i++)
        if(n%i==0)
            divisors[++tot]=i,divisors[++tot]=n/i;
    if(i*i==n)
        divisors[++tot]=i;
    sort(divisors+1,divisors+tot+1);
}
namespace IStream{
    #define L (1<<16)
    char Get_Char()
    {
        static char buffer[L],*S,*T;
        if(S==T)
        {
            T=(S=buffer)+fread(buffer,1,L,stdin);
            if(S==T) return EOF;
        }
        return *S++;
    }
    int Get_Int()
    {
        int re=0;
        char c=Get_Char();
        while(c<‘0‘||c>‘9‘)
            c=Get_Char();
        while(c>=‘0‘&&c<=‘9‘)
            re=(re<<1)+(re<<3)+(c-‘0‘),c=Get_Char();
        return re;
    }
}
int main()
{
    //freopen("3004.in","r",stdin);
    //freopen("3004.out","w",stdout);
    using namespace IStream;
    int T,i,j;
    cin>>n;
    Decomposition(n);
    for(T=1;T<=10;T++)
    {
        printf("Case #%d:\n",T);
        Initialize();
        for(i=2;i<=n;i++)
        {
            if(T==1)
                fa[i]=Get_Int();
            else
                fa[i]=(fa[i]+19940105)%(i-1)+1;
        }
        for(i=n;i;i--)
            size[fa[i]]+=++size[i];
        for(i=1;i<=n;i++)
            f[size[i]]++;
        for(i=1;i<=tot;i++)
        {
            int temp=0;
            for(j=divisors[i];j<=n;j+=divisors[i])
                temp+=f[j];
            if(temp==n/divisors[i])
                printf("%d\n",divisors[i]);
        }
    }
    return 0;
}
时间: 2024-07-29 16:03:03

BZOJ 3004 吊灯 树形DP的相关文章

bzoj 1131 简单树形dp

思路:随便想想就能想出来啦把...  卡了我一个vector... #include<bits/stdc++.h> #define LL long long #define fi first #define se second #define mk make_pair #define pii pair<int,int> #define piii pair<int, pair<int,int> > using namespace std; const int

BZOJ 4753 二分+树形DP

思路: 先二分答案 f[x][j]表示在x的子树里选j个点 f[x][j+k]=max(f[x][j+k],f[x][j]+f[v[i]][k]); 初始化 x!=0 -> f[x][1]=p[x]-s[x]*mid x=0 -> f[x][0]=0 类似4033的那样转移 看似O(n^3)实际O(n^2) 加一个二分 复杂度O(能过) //By SiriusRen #include <cstdio> #include <cstring> #include <al

BZOJ 1093 最大半连通子图(强连通分量+树形DP)

题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=1093 题意: 思路:(1)首先,强连通分量中的一个点若在最大半连通子图中,则必定整个连通分量中的点都在,因为都在还是满足半连通的性质而且使得节点数更多. (2)因此,求出强连通分量缩点,形成一个有向无环图,其实与树是差不多的.在这个图上DP一次即可,也就是找出最长链以及最长链的个数. vector<int> g[N],g1[N]; int n,m,mod; int dfn[N],lo

BZOJ 1924 所驼门王的宝藏(强连通分量+树形DP)

题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=1924 题意: 思路:首先建立所有可达点之间的有向图.之后求强连通分量SCC,缩点重新构图.然后就是一个树,树形DP一下即可. int n,r,c; map<i64,int> mp; map<int,int> mp1,mp2; struct node { int x,y,op; }; node a[N]; int visit[N]; vector<int> V1

[BZOJ 4033] [HAOI2015] T1 【树形DP】

题目链接:BZOJ - 4033 题目分析 使用树形DP,用 f[i][j] 表示在以 i 为根的子树,有 j 个黑点的最大权值. 这个权值指的是,这个子树内部的点对间距离的贡献,以及 i 和 Father[i] 之间的边对答案的贡献(比如这条边对黑点对距离和的贡献就是子树内部的黑点数 * 子树外部的黑点数 * 这条边的权值). 然后DFS来求,枚举 i 的每个儿子 j,现在的 f[i][] 是包含了 [1, j-1] 子树,然后两重循环枚举范围是 [1, j - 1] 的子树总 Size 和

bzoj 3566: [SHOI2014]概率充电器 树形DP

首先普及一个概率公式 P(A+B)=P(A)+P(B)-P(AB) 题意:一些充电元件和导线构成一棵树,充电元件是否能充电有2种情况, 1.它自己有qi%的概率充电 2.与它相邻的元件通过导线给它充电(导线有p%的概率导通) 求最终充了电的元件的期望 题解:首先可以将元件能否充电分成3种情况考虑, 1.它自己给自己充好了电 2.它的儿子方向给它传送了电 3.它的父亲方向给它传送了电. 对于1,题目已经给出可以直接赋值, 对于2,可以通过一次树的深度遍历求得.pson[now]=pson[now]

BZOJ 2878([Noi2012]迷失游乐园-树形DP+环加外向树+期望DP+vector的erase)

2878: [Noi2012]迷失游乐园 Time Limit: 10 Sec  Memory Limit: 512 MBSec  Special Judge Submit: 319  Solved: 223 [Submit][Status] Description 放假了,小Z觉得呆在家里特别无聊,于是决定一个人去游乐园玩.进入游乐园后,小Z看了看游乐园的地图,发现可以将游乐园抽象成有n个景点.m条道路的无向连通图,且该图中至多有一个环(即m只可能等于n或者n-1).小Z现在所在的大门也正好是

BZOJ 2878: [Noi2012]迷失游乐园( 树形dp )

一棵树的话直接树形dp(求出往下走和往上走的期望长度). 假如是环套树, 环上的每棵树自己做一遍树形dp, 然后暴力枚举(环上的点<=20)环上每个点跑经过环上的路径就OK了. --------------------------------------------------------------------------------------------- #include<cstdio> #include<cstring> #include<algorithm&

BZOJ 2435 道路修建 NOI2011 树形DP

一看到这道题觉得很水,打了递归树形DP后RE了一组,后来发现必须非递归(BFS) 递归版本84分: #include<cstdio> #include<cstring> #include<algorithm> using namespace std; int N,point[1000003],next[2000003],v[2000003],c[2000003],cnt=0,f[1000003]; bool p[1000003]; long long sum=0; vo