HDU

来源:互联网 发布:淘宝店铺链接在哪复制 编辑:程序博客网 时间:2024/06/07 01:32

传送门
// 一棵树, 上面都有一个点权, 问有多少条路径满足进过的点的类型刚好有k种,
// 首先这个肯定是点分治, 但是难点和POJ那题的不同处就是这个是状态, 那么就不能像POJ那题进行排序选点了, 所以我们应该要换一种比较高效的方法. 首先是k只有10, 那么我们进行状压, 那么我们要求的就是路径上这条路与( | )起来等于( 1<< k) - 1, 那么我们肯定不能n方的找出两条路径来判断是否满足这个条件, 而应该是利用高位前缀和的思想.
因为我们要求的是某一条路径和已知的路径或起来的值, 即是已知路径中的二进制表示中只要为0的地方是1就行了. 所以就用到了高位前缀和, 比如dp[100] = dp[100] + dp[101] + dp[110] + dp[111]; 这样我们就可以直接算出答案从而优化复杂度. 细节请看代码.

AC Code

const int maxn = 5e4+5;int n, cnt, head[maxn], k, vis[maxn], root, maxx, dis[maxn];int num, tot, siz[maxn], mv[maxn];int a[maxn];struct node {    int to, w, next;} e[maxn<<1];void add(int u, int v, int w){    e[cnt] = (node){v,w,head[u]};    head[u] = cnt++;}void getroot(int u, int fa){    siz[u] = 1, mv[u] = 0;    for (int i = head[u]; ~i; i = e[i].next) {        int to = e[i].to;        if (to == fa || vis[to]) continue;        getroot(to, u);        siz[u] += siz[to];        mv[u] = max(mv[u], siz[to]);    }    mv[u] = max(mv[u], tot - siz[u]);    if (mv[u] < mv[root]) root = u;}int dp[1030]; //这个不能开太大,否则会T.void getdis(int u,int fa,int dep){    dp[dep]++;    dis[++num] = dep;    for (int i = head[u]; ~i; i = e[i].next) {        int to = e[i].to;        if (to == fa || vis[to]) continue;        getdis(to, u, dep | (1 << a[to]));    }}ll ans;ll cal(int u,int f){    num = 0; Fill(dp,0);    getdis(u,-1,f | (1 << a[u]));    for(int i=0;i<k;i++){        for(int j = 0 ; j < (1<<k) ; j++) {            if(!((1<<i) & j)) dp[j] += dp[j | (1 << i)];        }    }    ll res = 0;    for(int i=1;i<=num;i++){        int tmp = dis[i];        tmp ^= (1<<k)-1;        res += dp[tmp];    }    return res;}void work(int u){    vis[u] = 1;    ans += cal(u, 1 << a[u]);    for (int i = head[u]; ~i; i = e[i].next) {        int to = e[i].to;        if (vis[to]) continue;        ll tmp = cal(to, 1 << a[u]);        ans -= tmp;        mv[root=0] = tot = siz[to];        getroot(to, -1);        work(root);    }}void solve(){    while(~scanf("%d%d",&n,&k)){        cnt = 0 ; Fill(head,-1); Fill(vis,0);        for(int i=1;i<=n;i++) {            scanf("%d",&a[i]);            a[i]--;        }        for (int i = 1; i < n; i++) {            int u, v, w;            scanf("%d%d",&u,&v);            add(u, v, 0); add(v, u, 0);        }        ans = 0;        mv[root=0] = tot = n;        getroot(1, -1);        work(root);        cout << ans << endl;    }}