Description
思源湖畔有一棵树,那是独孤玉溪最喜欢的地方。
传说中,这棵不见边际的树有N个节点,每个节点都有1片叶子,每片叶子都拥有K种颜色中的一种,独孤玉溪喜欢爬到这棵树上,沿着一条路线摘叶子,并拥有所有颜色的叶子。
独孤玉溪会选择一个起点,并沿着树边走,然后最终停在一个终点上(起点和终点可能相同),当然了每一个结点只能经过一次(每一片叶子只能摘一遍)。独孤玉溪突生奇想,有多少种不同的方案能满足自己呢?(两种方案不同当且仅当起点不同或终点不同)。
20%的数据:N<=10000,K<=10
40%的数据:N<=50000,K<=2
100%的数据:N<=50000,K<=10
Solution
树形DP是非常明显的想法,需要卡一下空间。
也可以容斥,正难则反,设不存在某几种颜色的的状态,O(N)搜索联通块直接算,然后容斥。
还可以点分治,注意要把2k压成2k/2,二进制分成两半处理
Code
此处为容斥
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cmath>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fod(i,a,b) for(int i=a;i>=b;i--)
#define N 50005
using namespace std;
int fs[N],nt[2*N],dt[2*N],n,cl[N],m,m1,ans;
bool bz[11],b[N];
int sqr(int x)
{
return x*x;
}
int fd(int k)
{
int s=1;
for(int i=fs[k];i;i=nt[i])
{
int p=dt[i];
if(!bz[cl[p]]&&!b[p]) b[p]=1,s+=fd(p);
}
return s;
}
void get(int v,int l)
{
if(l)
{
memset(b,0,sizeof(b));
fo(i,1,n) if(!b[i]&&!bz[cl[i]]) b[i]=1,ans+=v*sqr(fd(i));
}
fo(i,l+1,m) bz[i]=1,get(-v,i),bz[i]=0;
}
void link(int x,int y)
{
nt[++m1]=fs[x];
dt[fs[x]=m1]=y;
}
int main()
{
cin>>n>>m;
fo(i,1,n) scanf("%d",&cl[i]);
fo(i,1,n-1)
{
int x,y;
scanf("%d %d",&x,&y);
link(x,y),link(y,x);
}
ans=0;
get(-1,0);
cout<<n*n-ans;
}