HDU 5977 Garden of Eden【伊甸园】

前言

时间紧,就只写最关键的高维前缀和部分

介绍

其实我一开始也不知道这样一个东西,但我也做出来了,自己yy
把他想成一个dp,毕竟前缀和本身就是最短的dp.
dp有一个思想就是我们一定要充分的假设dp[i][j]是未知的,但推出这个状态的式子是已知的,尽管你觉得这样一个推出这个状态的式子本身不可求。
很简单的,就是要求 d p [ ( 1010... ) 2 ] 这样一个式子的值,意思就是括号内二进制数的出现次数与改二进制数上任意1个或多个位置的0变成1的新二进制数出现的次数的和。(有点拗口)。假设该二进制数为 ( 1010 ) 2 如果限定所有二进制数的最高位为第4位,那么现在这个式子就不用求了,如果再增加一位至第五位,那么他表示的其实是 d p [ ( 01010 ) 2 ] 的状态,还差一个 d p [ ( 11010 ) 2 ] 的值,加上去就好了。
这个东西也出现在或的贪心里。复杂度O(nlogn);

题面

一大早过来补题面。意思就是求有多少条树上路径包含所有颜色的树上路径。每个点都有颜色.

解法

点分治不用想了。这道题唯一的难点上面已经讲了,唯一的细节就是权在点上,仔细想想加点权的顺序。具体操作看代码。

代码

#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int _ =5e4+4,INF = 2e9;
struct edge{
    int to,nt;
}e[_<<1];
int head[_],size[_],root;
bool vis[_];
int n,k,cnt,all,MX,K,col[_];
LL ans,tong1[1025],tong2[1025];
inline void add(register int a,register int b){
    e[++cnt].to=a,e[cnt].nt=head[b],head[b]=cnt;
}
void getroot(register int now,register int fa){
    int mx=0;size[now]=1;
    for(register int i=head[now];i;i=e[i].nt){
        if(vis[e[i].to]||e[i].to==fa)continue;
        getroot(e[i].to,now);
        size[now]+=size[e[i].to];
        if(size[e[i].to]>mx)mx=size[e[i].to];
    }
    mx=max(mx,all-size[now]);
    if(MX>mx)root=now,MX=mx;
    return;
}
void dfs(register int now,register int fa,register int len){
    for(register int i=head[now];i;i=e[i].nt){
        if(e[i].to==fa)continue;
        if(vis[e[i].to])continue;
        tong1[len|(1<<col[e[i].to])]++,tong2[len|(1<<col[e[i].to])]++;
        dfs(e[i].to,now,len|(1<<col[e[i].to]));
    }
}
inline LL getdis(register int now,register int len){
    tong1[(1<<col[now])|len]++,tong2[(1<<col[now])|len]++;
    dfs(now,0,(1<<col[now])|len);
    //cout<<now<<' '<<len<<endl;
    /*for(register int i=0;i<=K;++i){
        cout<<tong1[i]<<' ';
    }
    cout<<endl;*/
    for(register int i=0;i<k;++i){
        for(register int j=K;j>=0;--j){
            if(!((1<<i)&j))tong2[j]+=tong2[(1<<i)|j];
        }
    }
    LL ret=0;
    for(register int i=0;i<=K;++i)ret+=tong1[i]*(tong2[(i^K)]);
    //ret+=tong1[k]*(tong2[0]-1);
    for(register int i=0;i<=K;++i)tong1[i]=tong2[i]=0;
    //cout<<ret<<endl;
    return ret;
}
void divide(register int now){
    vis[now]=1;
    ans+=getdis(now,0);
    for(register int i=head[now];i;i=e[i].nt){
        if(vis[e[i].to])continue;
        ans-=getdis(e[i].to,(1<<col[now]));
        all=size[e[i].to];MX=INF;
        getroot(e[i].to,now);
        divide(root);
    }
}
int main(){
    //freopen("data.in","r",stdin);
    while(~scanf("%d%d",&n,&k)){

        memset(head,0,sizeof(head));
        K=(1<<k)-1;
        memset(vis,0,sizeof(vis));cnt=0;ans=0;
        for(register int i=1;i<=n;++i)scanf("%d",&col[i]),col[i]--;
        for(register int i=1;i<n;++i){
            register int a,b;
            scanf("%d%d",&a,&b);
            add(a,b);add(b,a);
        }
        all=n;MX=INF;
        getroot(1,0);

        divide(root);
        printf("%lld\n",ans);
    }
    return 0;
}

总结

感谢YCB推荐这道稍微没有那么板子的点分治,刚刚适合我这种蒟蒻。。

猜你喜欢

转载自blog.csdn.net/JH_2002/article/details/81267429