【CodeForces1336】简要题解

比赛传送门

A. Linova and Kingdom

容易注意到我们在一个位置放下一个工业城市的贡献是 d e p − s i z dep-siz depsiz d e p dep dep 表示它到根还有多少个点没有放, s i z siz siz 表示它子树内有多少个点放了。

显然我们每次只会选择剩下的子树的叶子,贪心即可。

代码:

#include<bits/stdc++.h>
#define ll long long
#define re register
#define cs const

namespace IO{

inline char gc(){
	static cs int Rlen=1<<22|1;static char buf[Rlen],*p1,*p2;
	return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
}template<typename T>T get_integer(){
	char c;bool f=false;while(!isdigit(c=gc()))f=c=='-';T x=c^48;
	while(isdigit(c=gc()))x=((x+(x<<2))<<1)+(c^48);return f?-x:x;
}inline int gi(){return get_integer<int>();}

}using namespace IO;

using std::cerr;
using std::cout;
using pii=std::pair<int,int>;
#define fi first
#define se second

cs int N=2e5+7;

int n,k;

std::vector<int> G[N];
inline void adde(int u,int v){
	G[u].push_back(v);
	G[v].push_back(u);
}
int fa[N],sz[N],d[N],son[N];

void dfs(int u,int p){
	fa[u]=p,d[u]=d[p]+1;
	for(int v:G[u])if(v!=p){
		dfs(v,u);sz[u]+=sz[v];
		++son[u];
	}++sz[u];
}

ll ans=0;
std::priority_queue<pii> q;

void Main(){
	n=gi(),k=gi();
	for(int re i=1;i<n;++i)
		adde(gi(),gi());
	dfs(1,0);
	for(int i=1;i<=n;++i)
		if(!son[i])q.push({d[i]-1,i});
	while(k--){
		auto t=q.top();q.pop();
		ans+=t.fi;son[fa[t.se]]--;
		if(!son[fa[t.se]]){
			int u=fa[t.se];
			q.push({d[u]-sz[u],u});
		}
	}cout<<ans<<"\n";
}

inline void file(){
#ifdef zxyoi
	freopen("C.in","r",stdin);
#endif
}signed main(){file();Main();return 0;}

B. Xenia and Colorful Gems

当我们确定了两端之后,中间那个的选择一定是最接近平均数的。

那反过来,确定中间那个之后,两端的一定是最接近它的。

维护前缀最小值和后缀最大值即可。

代码:

#include<bits/stdc++.h>
#define ll long long
#define re register
#define cs const

namespace IO{

inline char gc(){
	static cs int Rlen=1<<22|1;static char buf[Rlen],*p1,*p2;
	return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
}template<typename T>T get_integer(){
	char c;bool f=false;while(!isdigit(c=gc()))f=c=='-';T x=c^48;
	while(isdigit(c=gc()))x=((x+(x<<2))<<1)+(c^48);return f?-x:x;
}inline int gi(){return get_integer<int>();}

}using namespace IO;

using std::cerr;
using std::cout;
using pii=std::pair<int,int>;
#define fi first
#define se second

cs int N=3e5+7;

ll ans;
int pr[N],sf[N];

ll sqr(int x){
	return (ll)x*x;
}

ll calc(int x0,int x1,int x2){
	return sqr(x0-x1)+sqr(x1-x2)+sqr(x2-x0);
}

std::vector<pii> vec;

void solve(int t){
	pr[0]=-1;int _0=(t+2)%3,_1=t,_2=(t+1)%3;
	for(size_t re i=0;i<vec.size();++i)
		if(vec[i].se==_0)pr[i]=vec[i].fi;
		else if(i)pr[i]=pr[i-1];
	sf[vec.size()]=-1;
	for(size_t re i=vec.size()-1;~i;--i)
		if(vec[i].se==_2)sf[i]=vec[i].fi;
		else sf[i]=sf[i+1];
	for(size_t re i=0;i<vec.size();++i)
		if(vec[i].se==_1){
			if(pr[i]!=-1&&sf[i]!=-1)
				ans=std::min(ans,calc(pr[i],sf[i],vec[i].fi));
		}
	pr[0]=sf[vec.size()]=-1;
	for(size_t re i=0;i<vec.size();++i)
		if(vec[i].se==_2)pr[i]=vec[i].fi;
		else if(i)pr[i]=pr[i-1];
	sf[vec.size()]=-1;
	for(size_t re i=vec.size()-1;~i;--i)
		if(vec[i].se==_0)sf[i]=vec[i].fi;
		else sf[i]=sf[i+1];
	for(size_t re i=0;i<vec.size();++i)
		if(vec[i].se==_1){
			if(pr[i]!=-1&&sf[i]!=-1)
				ans=std::min(ans,calc(pr[i],sf[i],vec[i].fi));
		}
}

void solve(){
	vec.clear();ans=4e18;
	int n1=gi(),n2=gi(),n3=gi();
	for(int re i=1;i<=n1;++i)
		vec.push_back({gi(),0});
	for(int re i=1;i<=n2;++i)
		vec.push_back({gi(),1});
	for(int re i=1;i<=n3;++i)
		vec.push_back({gi(),2});
	std::sort(vec.begin(),vec.end());
	solve(0),solve(1),solve(2);
	cout<<ans<<"\n";
}

void Main(){int T=gi();while(T--)solve();}
inline void file(){
#ifdef zxyoi
	freopen("D.in","r",stdin);
#endif
}signed main(){file();Main();return 0;}

C. Kaavi and Magic Spell

T T T S S S 短的地方用通配符填上。

考虑如下的设 d p [ i ] [ j ] dp[i][j] dp[i][j] 表示用了 S S S 的前 i i i 个字符,匹配了 T [ j : j + i − 1 ] T[j:j+i-1] T[j:j+i1] 的方案数。

转移讨论一下插在前面还是后面即可。

代码:

#include<bits/stdc++.h>
#define ll long long
#define re register
#define cs const

using std::cerr;
using std::cout;

cs int mod=998244353;
inline int add(int a,int b){return a+b>=mod?a+b-mod:a+b;}
inline int dec(int a,int b){return a-b<0?a-b+mod:a-b;}
inline int mul(int a,int b){return (ll)a*b%mod;}
inline void Inc(int &a,int b){a+=b-mod;a+=a>>31&mod;}
inline void Dec(int &a,int b){a-=b;a+=a>>31&mod;}

cs int N=3e3+7;

int tl,sl,ans;
char t[N],s[N];

int dp[N][N],f[N][N];

void Main(){
	scanf("%s%s",s+1,t+1);
	tl=strlen(t+1),sl=strlen(s+1);
	for(int re j=1;j<=sl;++j)
		dp[1][j]=2*((j<=tl&&s[1]==t[j])||(j>tl));
	for(int re i=2;i<=sl;++i){
		for(int re j=1;j<=sl;++j){
			//1 : add at the beginning
			if(j>tl)Inc(dp[i][j],dp[i-1][j+1]);
			else {
				if(s[i]==t[j])
					Inc(dp[i][j],dp[i-1][j+1]);
			}
			//2 : add at the end
			int k=j+i-1;
			if(k>tl)Inc(dp[i][j],dp[i-1][j]);
			else {
				if(s[i]==t[k])
					Inc(dp[i][j],dp[i-1][j]);
			}
		}
	}int ans=0;
	for(int re i=tl;i<=sl;++i)
		Inc(ans,dp[i][1]);
	cout<<ans<<"\n";
}

inline void file(){
#ifdef zxyoi
	freopen("E.in","r",stdin);
#endif
}signed main(){file();Main();return 0;}

D. Yui and Mahjong Set

加入一张牌之后增加的刻子数量只和这张牌原来的数量有关,但当增加 0 0 0 张的时候,我们无法判断原来张数是 0 0 0 还是 1 1 1

加入 i i i 之后增加的顺子数量只和 [ i − 2 , i + 2 ] [i-2,i+2] [i2,i+2] 有关。

如果直接ins 1-n ,得到的是若干个非线性方程,如果原来的数全部是 0 0 0 1 1 1 的话可能只能通过爆搜来解。

考虑如下的询问:插入 n n n ,插入 n − 1 n-1 n1 ,插入 n n n

首先由于插入了两次,此时 n n n 的数量是大于等于 2 2 2 的,可以通过增加的刻子数确定 n n n 的数量,然后,两次插入 n n n 之间插入了一个 n − 1 n-1 n1,那么两次增加的顺子数量之差就是 n − 2 n-2 n2 的数量,然后通过增加的顺子数量,我们可以知道 n − 1 n-1 n1 的数量。再通过插入 n − 1 n-1 n1 时增加的顺子数量可以确定 n − 3 n-3 n3 的数量。

然后插入 2 2 2 ( n − 2 ) (n-2) (n2) 我们得到每个时候增加的顺子数量。

当我们知道 i + 1 i+1 i+1 以上的所有信息的时候,我们可以通过插入 i + 2 i+2 i+2 时候增加的顺子数量得到 i i i 的数量。从 n − 4 n-4 n4 开始倒着推即可。


代码:

#include<bits/stdc++.h>
#define re register
const int N=1e2+7;
int n,s[N],t[N],a[N],s1,t1,s2,t2,s3,t3,sl,tl,sn,tn;
void qry(int i,int &t,int &s){
	std::cout<<"+ "<<i<<std::endl;
	std::cin>>tn>>sn;t=tn-tl,s=sn-sl;
	tl=tn,sl=sn;
}int find(int x){
	for(int re i=1;;++i)
		if(i*(i-1)==x+x)return i;
}
signed main(){
	std::cin>>n>>tl>>sl;
	for(int re i=2;i<n-1;++i)
		qry(i,t[i],s[i]);
	qry(n,t1,s1);qry(n-1,t2,s2);qry(n,t3,s3);
	a[n]=find(t3)-1;a[n-2]=s3-s1-1;a[n-1]=s3/(a[n-2]+1)-1;
	a[n-3]=(s2-(a[n-2]+1)*(a[n]+1))/(a[n-2]+1)-1;
	for(int re i=n-4;i;--i)
		a[i]=(s[i+2]-((a[i+1]+1)*a[i+3]+a[i+3]*a[i+4]))/(a[i+1]+1)-1;
	++a[1];
	std::cout<<"! ";for(int re i=1;i<=n;++i)
		std::cout<<a[i]<<" ";
	return 0;
}

E. Chiori and Doll Picking

首先建立线性基,我们知道基内所有xor结果出现的次数都是 2 n − k 2^{n-k} 2nk,其中 k k k 是基的秩。

我们发现 2 m 2^m 2m 跑不过去,但是 2 m / 2 2^{m/2} 2m/2 跑得过去。

当基的数量 k ≤ m / 2 k\leq m/2 km/2 的时候,直接暴力搜索所有xor结果即可。

考虑当 k > m / 2 k> m/2 k>m/2 的时候怎么做。

S a Sa Sa 表示原线性基所有 xor 的结果, ∣ s ∣ |s| s 表示 p o p c o u n t ( s ) popcount(s) popcount(s)

设集合幂级数 A s = [ s ∈ S a ] , F s c = [ ∣ s ∣ = c ] A_s=[s\in Sa],F^c_s=[|s|=c] As=[sSa],Fsc=[s=c]

p c p_c pc 表示 S a Sa Sa p o p c o u n t popcount popcount c c c 的数量。不难发现 p c p_c pc 的值就是 A A A F c F^c Fc 的xor卷积的 ∅ \empty 项系数。

我们知道 I F W T IFWT IFWT 后的 ∅ \empty 项系数就是 I F W T IFWT IFWT 之前的点值之和除掉 2 m 2^m 2m

为了方便,用 A ^ \hat{A} A^ 表示 A A A 做一次 F W T FWT FWT 之后的数列。

于是我们要求的就是 A ^ \hat A A^ F c ^ \hat{F^c} Fc^ 的点积。

冷静一下或者打个表可以发现 F c ^ s \hat{F^c}_s Fc^s 有一个非常强的性质,就是其值可以用一个只和 ∣ s ∣ |s| s 有关的式子表示出来。

F c ^ s \hat{F^c}_s Fc^s F W T FWT FWT 的形式写出来。

F c ^ S = ∑ T ( − 1 ) ∣ S ∩ T ∣ [ ∣ T ∣ = c ] \hat {F^c}_S=\sum_{T}(-1)^{|S\cap T|}[|T|=c] Fc^S=T(1)ST[T=c]

∣ S ∣ = j |S|=j S=j 考虑枚举 k = ∣ S ∩ T ∣ k=|S\cap T| k=ST

F c ^ S = ∑ k = 0 j ( − 1 ) k ∑ ∣ T ∣ = c , ∣ S ∩ T ∣ = k 1 \hat{F^c}_S=\sum_{k=0}^j(-1)^k\sum_{|T|=c,|S\cap T|=k}1 Fc^S=k=0j(1)kT=c,ST=k1

考虑后面那个东西,不难发现可以用组合数来表示,首先在 j j j 个位置里面选 k k k 个置为 1 1 1,然后在 m − j m-j mj 个位置里面选 c = k c=k c=k 个置为 1 1 1,即 ( j k ) ⋅ ( m − j c − k ) {j\choose k}\cdot{m-j\choose c-k} (kj)(ckmj)

现在我们知道所有 ∣ S ∣ |S| S 相同的位置在 F ^ c \hat F^c F^c 中都是相同的,也就意味着我们只需要考虑对于所有 j j j ∣ S ∣ = j |S|=j S=j A ^ S \hat A_S A^S 之和。

这一部分仍然非常巧妙,设 ⊗ \otimes 表示异或(也就是集合的对称差),我们知道有

∣ S & W ∣ + ∣ T & W ∣ ≡ ∣ ( S ⊗ T ) & W ∣ ( m o d 2 ) |S\&W|+|T\&W|\equiv |(S\otimes T)\&W|\pmod 2 S&W+T&W(ST)&W(mod2)

这个在接下来讨论 − 1 -1 1 的幂次的时候非常有用。

F W T FWT FWT 写出 A ^ S \hat A_S A^S 的式子:

A ^ S = ∑ T ( − 1 ) ∣ S ∩ T ∣ [ T ∈ S a ] \hat A_S=\sum_T(-1)^{|S\cap T|}[T\in Sa] A^S=T(1)ST[TSa]

我们直接考虑 T T T 是由哪些基构成的:

A ^ S = ∑ W ⊆ 2 K ( − 1 ) ∣ S ∩ ⊗ j ∈ W B j ∣ = ∑ W ⊆ 2 K ∏ j ∈ W ( − 1 ) ∣ S ∩ B j ∣ \hat A_S=\sum_{W\subseteq 2^K}(-1)^{|S\cap \otimes_{j\in W}B_j|}=\sum_{W\subseteq 2^K}\prod_{j\in W}(-1)^{|S\cap B_j|} A^S=W2K(1)SjWBj=W2KjW(1)SBj

有一点容斥的知识就知道,这样的 A ^ S \hat A_S A^S 只可能有两种值,要么是 0 0 0 要么是 2 K 2^K 2K,且值为 2 K 2^K 2K 当且仅当所有的 ∣ S ∩ B j ∣ |S\cap B_j| SBj 都是偶数。

还是根据上面那个式子: ∣ S & W ∣ + ∣ T & W ∣ ≡ ∣ ( S ⊗ T ) & W ∣ ( m o d 2 ) |S\&W|+|T\&W|\equiv |(S\otimes T)\&W|\pmod 2 S&W+T&W(ST)&W(mod2),我们考虑找到若干个线性无关的向量满足条件,然后异或起来就行了。

考虑如下的构造方式:

找到每一个在原线性基里面没有的行,设为 i i i ,则我们在新的元素的第 i i i 位为 1 1 1,除此以外位置 j j j 1 1 1 当且仅当线性基消元之后第 j j j 行第 i i i 位为 1 1 1

不难证明这样构造出来的向量线性无关且满足条件,且不存在其他线性无关的向量满足条件,这样的向量有 m − k m-k mk个。直接 2 m − k 2^{m-k} 2mk 求出所有 x o r xor xor 的结果即可。

代码:

#include<bits/stdc++.h>
#define ll long long
#define re register
#define cs const

namespace IO{

inline char gc(){
	static cs int Rlen=1<<22|1;static char buf[Rlen],*p1,*p2;
	return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
}template<typename T>T get_integer(){
	char c;bool f=false;while(!isdigit(c=gc()))f=c=='-';T x=c^48;
	while(isdigit(c=gc()))x=((x+(x<<2))<<1)+(c^48);return f?-x:x;
}inline int gi(){return get_integer<int>();}
inline ll gl(){return get_integer<ll>();}

}using namespace IO;

using std::cerr;
using std::cout; 

cs int mod=998244353,iv2=(mod+1)/2;
inline int add(int a,int b){return a+b>=mod?a+b-mod:a+b;} 
inline int dec(int a,int b){return a-b<0?a-b+mod:a-b;}
inline int mul(int a,int b){ll r=(ll)a*b;return r>=mod?r%mod:r;}
inline void Inc(int &a,int b){a+=b-mod;a+=a>>31&mod;}
inline void Dec(int &a,int b){a-=b;a+=a>>31&mod;}
inline void Mul(int &a,int b){a=mul(a,b);}
inline int po(int a,int b){int r=1;for(;b;b>>=1,Mul(a,a))if(b&1)Mul(r,a);return r;} 

cs int N=2e5+7,M=63;

int n,m,k;
ll b[N],t[N],tot;
int ans[M],ct[M];

void dfs(int nw,ll vl){
	if(nw==tot){
		++ct[__builtin_popcountll(vl)];
		return ;
	}dfs(nw+1,vl);dfs(nw+1,vl^t[nw+1]);
}

void Main(){
	n=gi(),m=gi();
	for(int re i=1;i<=n;++i){
		ll a=gl();
		for(int re j=m-1;~j;--j)
			if(a>>j&1){
				if(!b[j]){
					b[j]=a;++k;
					break;
				}a^=b[j];
			}
	}
	for(int re i=m-1;~i;--i)
		for(int re j=i-1;~j;--j)
			if(b[i]&(1ll<<j))
				b[i]^=b[j];
	if(k<=m/2){
		for(int re i=0;i<m;++i)
			if(b[i])t[++tot]=b[i];
		dfs(0,0);int pw=po(2,n-k);
		for(int re i=0;i<=m;++i)
			cout<<mul(ct[i],pw)<<" ";
	}else {
		for(int re i=0;i<m;++i)
			if(b[i]==0){
				ll &p=t[++tot];p=1ll<<i;
				for(int re j=i+1;j<m;++j)
					if(b[j]>>i&1)p|=1ll<<j;
			}
		dfs(0,0);static int C[M][M];
		for(int re i=0;i<=m;++i){
			C[i][0]=1;
			for(int re j=1;j<=i;++j)
				C[i][j]=add(C[i-1][j],C[i-1][j-1]);
		}
		for(int re i=0;i<=m;++i){
			for(int re j=0;j<=m;++j){
				int coef=0;
				for(int re k=0;k<=i&&k<=j;++k)
				(k&1)?
					Dec(coef,mul(C[j][k],C[m-j][i-k])):
					Inc(coef,mul(C[j][k],C[m-j][i-k]));
				Inc(ans[i],mul(coef,ct[j]));
			}
		}int pw=po(2,n-m+mod-1);
		for(int re i=0;i<=m;++i)
			cout<<mul(pw,ans[i])<<" ";
	}
}

inline void file(){
#ifdef zxyoi
	freopen("E.in","r",stdin);
#endif
}signed main(){file();Main();return 0;}

F. Journey

分为两种情况考虑,LCA相同的和不同的。

LCA不同的情况,交出来是一条祖先链,用一个树状数组统计答案。

LCA相同的情况,考虑怎么处理交出来的路径跨过LCA的情况,我们考虑枚举两条路径左端点的LCA,然后看一下右端点需要在什么地方才能产生贡献,DSU on tree+线段树合并即可

代码:

#include<bits/stdc++.h>
#define ll long long
#define re register
#define cs const

namespace IO{

inline char gc(){
	static cs int Rlen=1<<22|1;static char buf[Rlen],*p1,*p2;
	return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
}template<typename T>T get_integer(){
	char c;bool f=false;while(!isdigit(c=gc()))f=c=='-';T x=c^48;
	while(isdigit(c=gc()))x=((x+(x<<2))<<1)+(c^48);return f?-x:x;
}inline int gi(){return get_integer<int>();}

}using namespace IO;

using std::cerr;
using std::cout;
using pii=std::pair<int,int>;
#define f first
#define s second

cs int N=1.5e5+7;

int n,m,k;ll ans;
std::vector<int> G[N];
std::vector<pii> Q[N];

int fa[N],in[N],out[N];
int sz[N],son[N],top[N];
int d[N],ps[N],dfc;

void dfs1(int u,int p){
	fa[u]=p,d[u]=d[p]+1;
	for(int v:G[u])if(v!=p){
		dfs1(v,u);sz[u]+=sz[v];
		if(sz[v]>sz[son[u]])
			son[u]=v;
	}++sz[u];
}void dfs2(int u,int tp){
	top[u]=tp;ps[in[u]=++dfc]=u;
	if(son[u])dfs2(son[u],tp);
	for(int v:G[u])
		if(v!=fa[u]&&v!=son[u])
			dfs2(v,v);
	out[u]=dfc;
}

int LCA(int u,int v){
	while(top[u]!=top[v])
		d[top[u]]<d[top[v]]?v=fa[top[v]]:u=fa[top[u]];
	return d[u]<d[v]?u:v;
} 

int jump(int u,int k){
	while(k>d[u]-d[top[u]]){
		k-=d[u]-d[top[u]]+1;
		u=fa[top[u]];
	}return ps[in[u]-k];
}

namespace SGT{

cs int N=::N*40;

int lc[N],rc[N],sm[N],ct;
void ins(int &u,int l,int r,int p){
	if(!u)u=++ct;++sm[u];if(l==r)return;int m=(l+r)>>1;
	p<=m?ins(lc[u],l,m,p):ins(rc[u],m+1,r,p); 
}int qy(int u,int l,int r,int ql,int qr){
	if((ql<=l&&r<=qr)||!u)return sm[u];int m=(l+r)>>1;
	if(qr<=m)return qy(lc[u],l,m,ql,qr);
	if(m<ql)return qy(rc[u],m+1,r,ql,qr);
	return qy(lc[u],l,m,ql,qr)+qy(rc[u],m+1,r,ql,qr);
}void merge(int &u,int v){
	if(!u||!v){u|=v;return;}
	merge(lc[u],lc[v]);
	merge(rc[u],rc[v]);
	sm[u]+=sm[v];
} 

void ins(int &u,int p){ins(u,1,n,p);}
int qy(int u,int l,int r){return qy(u,1,n,l,r);}

void clear(){
	while(ct)lc[ct]=rc[ct]=sm[ct]=0,--ct;
}

}

namespace BIT{

int tr[N];

void add(int l,int r){
	for(int re p=l;p<=n;p+=p&-p)++tr[p];
	for(int re p=r+1;p<=n;p+=p&-p)--tr[p];
}int qy(int p){
	int r=0;for(;p;p&=p-1)r+=tr[p];
	return r;
}

}

int rt[N];
std::vector<int> q[N];
void solve_q(int u,int Rt){
	rt[u]=0;int od=std::max(0,k-d[u]+d[Rt]);
	for(int v:q[u]){
		if(d[v]>=od+d[Rt]){
			int p=jump(v,d[v]-d[Rt]-od);
			ans+=SGT::qy(rt[u],in[p],out[p]);
		}SGT::ins(rt[u],in[v]);
	}for(int w:G[u])if(Rt!=u||w!=son[u]){
		solve_q(w,Rt);
		if(q[w].size()>q[u].size())
			q[u].swap(q[w]),std::swap(rt[u],rt[w]);
		for(int v:q[w]){
			if(d[v]>=od+d[Rt]){
				int p=jump(v,d[v]-d[Rt]-od);
				ans+=SGT::qy(rt[u],in[p],out[p]);
			}q[u].push_back(v);
		}q[w].clear();
		SGT::merge(rt[u],rt[w]);
	}
}void solve(int u){
	for(int v:G[u])solve(v);
	for(auto &t:Q[u])
		ans+=BIT::qy(in[t.s]);
	for(auto &t:Q[u])
		if(d[t.s]>=d[u]+k){
			int p=jump(t.s,d[t.s]-d[u]-k);
			BIT::add(in[p],out[p]);
		}
	for(auto &t:Q[u])
		ans+=BIT::qy(in[t.f]);
	for(auto &t:Q[u])
		if(d[t.f]>=d[u]+k){
			int p=jump(t.f,d[t.f]-d[u]-k);
			BIT::add(in[p],out[p]);
		}
	for(auto &t:Q[u])
		q[t.f].push_back(t.s);
	solve_q(u,u);q[u].clear();SGT::clear();
}

void Main(){
	n=gi(),m=gi(),k=gi();
	for(int re i=1;i<n;++i){
		int u=gi(),v=gi();
		G[u].push_back(v);
		G[v].push_back(u);
	}dfs1(1,0);dfs2(1,1);
	for(int re i=2;i<=n;++i)
		G[i].erase(std::find(G[i].begin(),G[i].end(),fa[i]));
	for(int re i=1;i<=m;++i){
		int u=gi(),v=gi();
		if(in[u]>in[v])
			std::swap(u,v);
		if(in[v]>out[u])
			std::swap(u,v);
		Q[LCA(u,v)].push_back({u,v});
	}solve(1);cout<<ans<<"\n";
}

inline void file(){
#ifdef zxyoi
	freopen("F.in","r",stdin);
#endif
}signed main(){file();Main();return 0;} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值