Description
给一棵m个结点的无根树,你可以选择一个度数大于1的结点作为根,然后给一些结点(根、内部结点和叶子均可)着以黑色或白色。你的着色方案应该保证根结点到每个叶子的简单路径上都至少包含一个有色结点(哪怕是这个叶子本身)。 对于每个叶结点u,定义c[u]为从根结点从U的简单路径上最后一个有色结点的颜色。给出每个c[u]的值,设计着色方案,使得着色结点的个数尽量少。
Input
第一行包含两个正整数m, n,其中n是叶子的个数,m是结点总数。结点编号为1,2,…,m,其中编号1,2,… ,n是叶子。以下n行每行一个0或1的整数(0表示黑色,1表示白色),依次为c[1],c[2],…,c[n]。以下m-1行每行两个整数a,b(1<=a < b <= m),表示结点a和b 有边相连。
Output
仅一个数,即着色结点数的最小值。
Sample Input
5 3
0
1
0
1 4
2 5
4 5
3 5
0
1
0
1 4
2 5
4 5
3 5
Sample Output
2
HINT
M<=10000
N<=5021
结论:根选哪个点不影响答案
因为相邻的节点不可能相同颜色(要不然只用染其中一个就最优)
然后就瞎jb树形DP,f[i][0/1]表示把i染成0或1的最小代价
代码如下:
//MT_LI #include<cstdio> #include<cstring> #include<cstdlib> #include<algorithm> #include<cmath> #include<queue> #include<vector> #include<ctime> #include<map> #include<bitset> #include<set> #define ll long long #define mp(x,y) make_pair(x,y) #define pll pair<long long,long long> #define pii pair<int,int> using namespace std; inline int read() { int f=1,x=0;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } int stack[20]; inline void write(int x) { if(x<0){putchar('-');x=-x;} if(!x){putchar('0');return;} int top=0; while(x)stack[++top]=x%10,x/=10; while(top)putchar(stack[top--]+'0'); } inline void pr1(int x){write(x);putchar(' ');} inline void pr2(int x){write(x);putchar(' ');} struct node{ int x,y,next; }a[21000];int len,last[11000]; void ins(int x,int y){a[++len]=(node){x,y,last[x]};last[x]=len;} int c[21000],ru[21000]; int f[21000][2],n; void dp(int x,int fa) { f[x][0]=f[x][1]=1; if(x<=n) { f[x][c[x]]=1; f[x][c[x]^1]=1<<30; } for(int k=last[x];k;k=a[k].next) { int y=a[k].y; if(y==fa)continue; dp(y,x); f[x][0]+=min(f[y][0]-1,f[y][1]); f[x][1]+=min(f[y][1]-1,f[y][0]); } } int main() { int m=read();n=read(); len=0;memset(last,0,sizeof(last)); for(int i=1;i<=n;i++)c[i]=read(); int root; for(int i=1;i<m;i++) { int x=read(),y=read(); ins(x,y);ins(y,x);ru[x]++;ru[y]++; if(ru[x]-1&&x>n)root=x;if(ru[y]-1&&y>n)root=y; } dp(root,0); pr2(min(f[root][0],f[root][1])); return 0; }