HDU 5977 Garden of Eden【伊甸園】

前言

時間緊,就只寫最關鍵的高維前綴和部分

介紹

其實我一開始也不知道這樣一個東西,但我也做出來了,自己yy
把他想成一個dp,畢竟前綴和本身就是最短的dp.
dp有一個思想就是我們一定要充分的假設dp[i][j]是未知的,但推出這個狀態的式子是已知的,儘管你覺得這樣一個推出這個狀態的式子本身不可求。
很簡單的,就是要求dp[(1010...)2] 這樣一個式子的值,意思就是括號內二進制數的出現次數與改二進制數上任意1個或多個位置的0變成1的新二進制數出現的次數的和。(有點拗口)。假設該二進制數爲(1010)2 如果限定所有二進制數的最高位爲第4位,那麼現在這個式子就不用求了,如果再增加一位至第五位,那麼他表示的其實是dp[(01010)2] 的狀態,還差一個dp[(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推薦這道稍微沒有那麼板子的點分治,剛剛適合我這種蒟蒻。。

發佈了47 篇原創文章 · 獲贊 3 · 訪問量 7081
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章