给定一棵有n个节点的无根树,树上的每个点有一个非负整数点权。定义一条路径的价值为路径上的点权和-路径上的点权最大值。 给定参数PPP,我们想知道,有多少不同的树上简单路径,满足它的价值恰好是PPP的倍数。 注意:单点算作一条路径;u!=vu!=vu!=v时,(u,v)(u,v)(u,v)和(v,u)(v,u)(v,u)只算一次。
n<=1e5,P<=1e7n<=1e5 , P<=1e7n<=1e5,P<=1e7
这个,智商压制题。
其实只需要点分治之后按路径最大值排序,再来个简单的子树内去重即可。
AC Code\rm AC\ CodeAC Code
#include<bits/stdc++.h>
#define maxn 100005
#define LL long long
using namespace std;
int n,P,a[maxn];
int info[maxn],Prev[maxn<<1],to[maxn<<1],cnt_e=0;
void Node(int u,int v){ Prev[++cnt_e]=info[u],info[u]=cnt_e,to[cnt_e]=v; }
int mn,rt,vis[maxn],sz[maxn];
void dfs(int u,int ff,int tsz){
int mx=0;sz[u]=1;
for(int i=info[u],v;i;i=Prev[i]) if((v=to[i])!=ff && !vis[v]) dfs(v,u,tsz),mx=max(mx,sz[v]),sz[u]+=sz[v];
if((mx=max(mx,tsz-sz[u]))<mn) mn=mx,rt=u;
}
int gert(int u,int tsz){ return mn=0x3f3f3f3f,dfs(u,0,tsz),rt; }
LL ans = 0;
int c[maxn],Mx[maxn],Sm[maxn],cnt[10000007];
bool cmp(const int &u,const int &v){ return Mx[u]<Mx[v]; }
void ser(int u,int ff,int mx,int sm){
mx=max(mx,a[u]),sm=(sm+a[u])%P;
Mx[++c[0]]=mx,Sm[c[0]]=sm,sz[u]=1,c[c[0]]=c[0];
for(int i=info[u],v;i;i=Prev[i])
if((v=to[i])!=ff && !vis[v])
ser(v,u,mx,sm),
sz[u]+=sz[v];
}
void solve(int u){
c[0]=0,ser(u,0,0,0),sort(c+1,c+1+c[0],cmp);
for(int i=1;i<=c[0];i++)
ans += cnt[(P-(Sm[c[i]]-a[u]-Mx[c[i]])%P)%P],
cnt[(Sm[c[i]]%P+P)%P]++;
for(int i=1;i<=c[0];i++)
cnt[(Sm[c[i]]%P+P)%P]=0;
vis[u]=1;
for(int i=info[u],v;i;i=Prev[i])
if(!vis[v=to[i]]){
c[0]=0,ser(v,u,0,0),sort(c+1,c+1+c[0],cmp);
for(int i=1;i<=c[0];i++)
ans -= cnt[(P-(Sm[c[i]]+a[u]-Mx[c[i]])%P)%P],
cnt[(Sm[c[i]]%P+P)%P]++;
for(int i=1;i<=c[0];i++)
cnt[(Sm[c[i]]%P+P)%P]=0;
}
for(int i=info[u],v;i;i=Prev[i])
if(!vis[v=to[i]]) solve(gert(v,sz[v]));
}
int main(){
scanf("%d%d",&n,&P);
for(int i=1,u,v;i<n;i++) scanf("%d%d",&u,&v),Node(u,v),Node(v,u);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
solve(gert(1,n));
printf("%lld\n",ans+n);
}