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