【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
题解
很神奇的dp
可以发现每个叶子节点最终都对应数轴上的一段区间,如果能按某种顺序使得没有区间经过x/p则必然合法
设f[i][x][y],表示能否把i的子树放到y/x~(y+1)/x中,可以发现y/x~(y+1)/x被平均分配
那么对于i的每个儿子j,求出j放到每个区间的情况,做一遍匈牙利即可判断
时间复杂度:O(能过)
code
#include <bits/stdc++.h>
#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;
}