A. Linova and Kingdom
容易注意到我们在一个位置放下一个工业城市的贡献是 d e p − s i z dep-siz dep−siz, 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+i−1] 的方案数。
转移讨论一下插在前面还是后面即可。
代码:
#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] [i−2,i+2] 有关。
如果直接ins 1-n ,得到的是若干个非线性方程,如果原来的数全部是 0 0 0 或 1 1 1 的话可能只能通过爆搜来解。
考虑如下的询问:插入 n n n ,插入 n − 1 n-1 n−1 ,插入 n n n。
首先由于插入了两次,此时 n n n 的数量是大于等于 2 2 2 的,可以通过增加的刻子数确定 n n n 的数量,然后,两次插入 n n n 之间插入了一个 n − 1 n-1 n−1,那么两次增加的顺子数量之差就是 n − 2 n-2 n−2 的数量,然后通过增加的顺子数量,我们可以知道 n − 1 n-1 n−1 的数量。再通过插入 n − 1 n-1 n−1 时增加的顺子数量可以确定 n − 3 n-3 n−3 的数量。
然后插入 2 2 2 至 ( n − 2 ) (n-2) (n−2) 我们得到每个时候增加的顺子数量。
当我们知道 i + 1 i+1 i+1 以上的所有信息的时候,我们可以通过插入 i + 2 i+2 i+2 时候增加的顺子数量得到 i i i 的数量。从 n − 4 n-4 n−4 开始倒着推即可。
代码:
#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} 2n−k,其中 k k k 是基的秩。
我们发现 2 m 2^m 2m 跑不过去,但是 2 m / 2 2^{m/2} 2m/2 跑得过去。
当基的数量 k ≤ m / 2 k\leq m/2 k≤m/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=[s∈Sa],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)∣S∩T∣[∣T∣=c]
设 ∣ S ∣ = j |S|=j ∣S∣=j 考虑枚举 k = ∣ S ∩ T ∣ k=|S\cap T| k=∣S∩T∣
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=0∑j(−1)k∣T∣=c,∣S∩T∣=k∑1
考虑后面那个东西,不难发现可以用组合数来表示,首先在 j j j 个位置里面选 k k k 个置为 1 1 1,然后在 m − j m-j m−j 个位置里面选 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)⋅(c−km−j)
现在我们知道所有 ∣ 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∣≡∣(S⊗T)&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)∣S∩T∣[T∈Sa]
我们直接考虑 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=W⊆2K∑(−1)∣S∩⊗j∈WBj∣=W⊆2K∑j∈W∏(−1)∣S∩Bj∣
有一点容斥的知识就知道,这样的 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| ∣S∩Bj∣ 都是偶数。
还是根据上面那个式子: ∣ 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∣≡∣(S⊗T)&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 m−k个。直接 2 m − k 2^{m-k} 2m−k 求出所有 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;}