CF1402C Star Trek 题解

89 篇文章 1 订阅

传送门: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=ORjson(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
*/ 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值