传送门:C. Star Trek
首先如果D=0的化,直接dp就可以了。
d
p
i
dp_{i}
dpi表示i号点为根是否式必胜态。
d
p
i
=
O
R
j
∈
s
o
n
(
i
)
(
d
p
j
X
O
R
1
)
dp_{i}=OR_{j\in son(i)} (dp_j\ XOR\ 1)
dpi=ORj∈son(i)(dpj XOR 1)
首先看到这题的
D
D
D这么大,就可以发现这是一个矩阵乘法的优化dp。
那么怎么dp呢?
可以发现我们每一次连出去一个“虫洞”都会连到另一个树的根。
所以我们只关心根的状态。
我们假设
f
i
,
0
f_{i,0}
fi,0表示考虑到第i个宇宙,root的状态是0的方案数(注:如果连虫洞的方法一样,root不一样算作不同的方案)
我们发现,如果在一个必败态下连接一个必败态,它的状态就会反转,甚至可以影响到上面的状态。我们不妨设
c
0
c_0
c0表示如果root为0,有多少必败态的点(在此情况必败)反转后会影响到root,这个可以换根dp。只需要分析一下反转的性质就行了。
然后我们对于新的一棵树,我们分类讨论出root为0/1时,可以在哪一些点上连接什么状态。
时间复杂度
O
(
n
+
l
o
g
2
(
D
)
∗
2
3
)
O(n+log_2(D)*2^3)
O(n+log2(D)∗23)
COde:
/*
{By GWj
*/
#pragma GCC optimize(2)
#include<bits/stdc++.h>
#define rb(a,b,c) for(int a=b;a<=c;++a)
#define rl(a,b,c) for(int a=b;a>=c;--a)
#define LL long long
#define IT iterator
#define PB push_back
#define II(a,b) make_pair(a,b)
#define FIR first
#define SEC second
#define FREO freopen("check.out","w",stdout)
#define rep(a,b) for(int a=0;a<b;++a)
#define SRAND mt19937 rng(chrono::steady_clock::now().time_since_epoch().count())
#define random(a) rng()%a
#define ALL(a) a.begin(),a.end()
#define POB pop_back
#define ff fflush(stdout)
#define fastio ios::sync_with_stdio(false)
#define R(a) cin>>a
#define R2(a,b) cin>>a>>b
#define check_min(a,b) a=min(a,b)
#define check_max(a,b) a=max(a,b)
using namespace std;
const int INF=0x3f3f3f3f;
typedef pair<int,int> mp;
/*}
*/
struct MAT{
int n,m,a[3][3]={{0,0,0},{0,0,0},{0,0,0}};
};
MAT I;
const int MOD=1e9+7;
MAT mul(MAT A,MAT B){
MAT rest;
assert(A.m==B.n);
rest.n=A.n;
rest.m=B.m;
rb(i,1,rest.n)
rb(j,1,rest.m)
rb(k,1,A.m){
rest.a[i][j]+=1ll*A.a[i][k]*B.a[k][j]%MOD;
if(rest.a[i][j]>MOD) rest.a[i][j]-=MOD;
}
return rest;
}
MAT quick(MAT A,LL B){
if(!B) return I;
MAT rest=quick(A,B>>1);
rest=mul(rest,rest);
if(B&1){
rest=mul(rest,A);
}
return rest;
}
int n;
LL d;
const int MAXN= 1e5+10;
vector<int> g[MAXN];
int have_change[MAXN],have[2][MAXN],f[MAXN];
bool check(int have0,int have1){
return bool (have0);
}
int alr=0,c[2];
int sum[MAXN],zero_sum[MAXN];
void calc_affect(int now){
have_change[now]=0;
if(f[now]){
if(have[0][now]==1){
have_change[now]=zero_sum[now];
}
}
else{
have_change[now]=sum[now];
}
}
void dfs(int now,int prev){
have[0][now]=have[1][now]=have_change[now]=0;
for(auto it:g[now]){
if(it!=prev){
dfs(it,now);
have[f[it]][now]++;
}
}
f[now]=check(have[0][now],have[1][now]);
for(auto it:g[now]){
if(it!=prev){
sum[now]+=have_change[it]+(!f[it]);
if(!f[it]) zero_sum[now]+=have_change[it]+1;
}
}
calc_affect(now);
}
void change_root(int now,int prev){
int change,hav[2],f_;
change=have_change[now];
hav[0]=have[0][now];
hav[1]=have[1][now];
f_=f[now];
alr+=f_;
c[f_]+=change;
if(!f_)
c[0]++;
if(c[f_]>MOD) c[f_]-=MOD;
// cout<<"@@"<<now<<" "<<have[0][now]<<" "<<change<<endl;
int save,save0;
save=sum[now];
save0=zero_sum[now];
for(auto it:g[now]){
if(it!=prev){
sum[now]-=have_change[it]+(!f[it]);
if(!f[it]) zero_sum[now]-=have_change[it]+1;
have[f[it]][now]--;
f[now]=check(have[0][now],have[1][now]);
calc_affect(now);
have[f[now]][it]++;
sum[it]+=have_change[now]+(!f[now]);
if(!f[now]) zero_sum[it]+=have_change[now]+1;
f[it]=check(have[0][it],have[1][it]);
calc_affect(it);
change_root(it,now);
have_change[now]=change;
have[0][now]=hav[0];
have[1][now]=hav[1];
sum[now]=save;
zero_sum[now]=save0;
f[now]=f_;
}
}
}
int main(){
fastio;
I.n=I.m=2;
rb(i,1,2)
I.a[i][i]=1;
R2(n,d);
rb(i,2,n){
int u,v;
R2(u,v);
g[u].PB(v);
g[v].PB(u);
}
dfs(1,0);
// dfs(1,0);
change_root(1,0);
// cout<<"#"<<c[0]<<" "<<c[1]<<" "<<alr<<endl;
MAT base;
base.n=base.m=2;
base.a[1][1]=(1ll*(n-alr)*n%MOD-c[0]+c[1]+MOD)%MOD;
base.a[1][2]=1ll*(n-alr)*n%MOD;
base.a[2][2]=1ll*alr*n%MOD;
base.a[2][1]=(1ll*alr*n%MOD-c[1]+c[0]+MOD)%MOD;
base=quick(base,d-1);
MAT init;
init.n=2;
init.m=1;
init.a[1][1]=n-alr;
init.a[2][1]=alr;
init=mul(base,init);
LL rest=0;
int s1,s2;
s1=init.a[1][1];
s2=init.a[2][1];
if(f[1]){
rest+=1ll*(n-have_change[1])*(s1+s2)+1ll*have_change[1]*s2;
rest%=MOD;
}
else{
have_change[1]++;
rest+=1ll*have_change[1]*s1;
rest%=MOD;
}
cout<<rest<<endl;
return 0;
}
/*
3 2
1 2
2 3
#10368 338 527
*/