【SRM609】&jzoj3720. LotteryTree

题目描述

Description
你发行了一种彩票,并且有P 人买了它。现在你要决定谁中奖。

你已经决定了用一个有根树来选择优胜者。你需要做的事情被列在下面:

•参与者从1 到P 连续编号。

•首先,你将树画在一个矩形的白板上,并需要满足以下条件:

– 树上的每一个结点对应白板上的一个圆圈。圆圈很小以至于你可以忽略他们的大小。

– 树上的每条边对应连接两个圈的线段。没有两条边相交。

– 根节点一定画在白板的最上方。

– 对于每个结点,连向它们儿子们的边都要向下走。(换句话说,父亲必须画在儿子的上方。)

– 所有的叶子需画在白板的底边。

•接下来,你将白板的底边分成P 段,每个叶子被恰好一条线段包含。将1到P 这些个不同的数字分配给每个线段,然后将数字标注在线段内的叶子上。

•现在,你要重复下述过程:找到一个空结点X,它的所有儿子都标注了数字;如果有多个这样的结点,随机选择一个;然后在X 的儿子中随机选择一个,将这个儿子的数字标注在X 上;当根节点被标上数字时过程结束。

你会获得整数P 和用于描述你使用的树的数组tree。这棵树有N 个结点,从0 到N-1 编号。结点0 是树根。对于每个其他结点,它父亲的编号小于它的编号。更正式的说,对于在1 和N- 1 之间的i,tree[i] 是结点i 父亲的编号。你想要让抽奖公平,即,保证每个参与者有相同的机率赢取大奖。为此,你可以在合法的要求下选择怎样画这棵树,以及怎样分配数字到叶子上。回答“YES”(不需要引号)如果抽奖可以公平,否则回答“NO” 。

Input
多组数据,读入到文件结束。

每组数据第一行读入两个整数N 和P ——所用树的大小以及参与者人数。

第二行读入N- 1 个整数,描述数组tree;其中第i 个整数为tree[i]。

Output
每组数据输出单独一行一个单词“YES”或“NO” 。

Sample Input
4 3

0 0 0

10 2

0 0 0 1 1 2 2 3 3

10 3

0 0 1 1 2 2 4 4 4

9 3

0 0 1 1 1 3 3 3

Sample Output
YES

YES

NO

NO

Data Constraint
对于30% 的数据,P<= N <=10。

对于100%的数据,2<=P<=100,2<=N<=101,非叶子结点都有至少两个儿子,数据组数<=40

Hint

【SRM609】&jzoj3720. LotteryTree_第1张图片

题解

很神奇的dp

可以发现每个叶子节点最终都对应数轴上的一段区间,如果能按某种顺序使得没有区间经过x/p则必然合法

设f[i][x][y],表示能否把i的子树放到y/x~(y+1)/x中,可以发现y/x~(y+1)/x被平均分配

那么对于i的每个儿子j,求出j放到每个区间的情况,做一遍匈牙利即可判断

时间复杂度:O(能过)

code

#include 
#define fo(a,b,c) for (a=b; a<=c; a++)
#define fd(a,b,c) for (a=b; a>=c; a--)
using namespace std;

int a[201][2];
int b[102][102];
int c[102];
int ls[102];
int son[102];
bool bz[102][102][102];
bool Bz[102];
int n,p,i,j,k,l,len;
double s;

void New(int x,int y)
{
    ++len;
    a[len][0]=y;
    a[len][1]=ls[x];
    ls[x]=len;
}

void Dfs(int Fa,int t)
{
    int i;
    son[t]=0;
    
    for (i=ls[t]; i; i=a[i][1])
    if (a[i][0]!=Fa)
    {
        Dfs(t,a[i][0]);
        b[t][++son[t]]=a[i][0];
    }
}

bool xyl(int T,int n,int t)
{
    int i;
    
    fo(i,1,n)
    if (bz[T][t][i] && !Bz[i])
    {
        Bz[i]=1;
        
        if (!c[i] || xyl(T,n,c[i]))
        {
            c[i]=t;
            return 1;
        }
    }
    
    return 0;
}

bool work(int t)
{
    int i,j;
    
    fo(i,1,son[t]) c[i]=0;
    
    fo(i,1,son[t])
    {
        fo(j,1,son[t]) Bz[j]=0;
        
        if (i==3)
        n=n;
        
        if (!xyl(t,son[t],i))
        return 0;
    }
    
    return 1;
}

bool dfs(int Fa,int t,long long x,long long y)
{
    long long X=x*son[t],Y=y*son[t];
    int i,j;
    
    if (!(floor((double)(y+1)*p/x-0.0000001)-floor((double)y*p/x)))
    return 1;
    
    if (!son[t]) return 0;
    
    fo(i,1,son[t])
    {
        fo(j,0,son[t]-1)
        bz[t][i][j+1]=dfs(t,b[t][i],X,Y+j);
    }
    
    return work(t);
}

int main()
{
    freopen("tree.in","r",stdin);
    freopen("tree.out","w",stdout);
    
    while (scanf("%d%d",&n,&p)!=EOF)
    {
        memset(ls,0,sizeof(ls));
        len=0;
        
        fo(i,2,n)
        {
            scanf("%d",&j);++j;
            New(j,i);New(i,j);
        }
        Dfs(0,1);
        
        if (dfs(0,1,1,0))
        printf("YES\n");
        else
        printf("NO\n");
    }
    
    fclose(stdin);
    fclose(stdout);
    
    return 0;
}

你可能感兴趣的:(【SRM609】&jzoj3720. LotteryTree)