AC通道:http://codeforces.com/problemset/problem/715/C
【题目大意】
给定一个有N个点的树,问其中有多少条路径满足他们的边权连成的数对M取余为0。
其中gcd(M,10)=1。
【题解】
首先是点分治的套路,然后这题的主要难点在如何统计连通块中的答案。
对于x→y的路径,也就是x→root→y,我们可以处理出dis[x]和dis[y]分别表示x到root、root到y连成的数对mod取模的结果
那么在合并时,如果路径合法,则应满足以下条件:
即:
然后我们就可以愉快地使用dfs统计答案了。
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<ctime>
#include<cmath>
#include<algorithm>
#include<map>
using namespace std;
typedef long long ll;
#define FILE "read"
#define MAXN 1000010
#define INF 1000000000
#define up(i,j,n) for(ll i=j;i<=n;++i)
#define dn(i,j,n) for(ll i=j;i>=n;--i)
#define cmax(a,b) a=max(a,b)
#define cmin(a,b) a=min(a,b)
namespace INIT{
char buf[1<<15],*fs,*ft;
inline char getc(){return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;}
inline ll read(){
ll x=0,f=1; char ch=getc();
while(!isdigit(ch)) {if(ch=='-') f=-1; ch=getc();}
while(isdigit(ch)) {x=x*10+ch-'0'; ch=getc();}
return x*f;
}
}using namespace INIT;
struct node{ll y,next,v;}e[MAXN*2];
ll n,mod,sum,root,len,ans,Link[MAXN],dis[MAXN],inv[MAXN],p[MAXN],deep[MAXN],size[MAXN],f[MAXN],vis[MAXN];
map<ll,ll>cnt;
void insert(ll x,ll y,ll v){e[++len].next=Link[x];Link[x]=len;e[len].y=y;e[len].v=v;}
void exgcd(ll a,ll b,ll &x,ll &y){
if(!b) {x=1;y=0;return;}
exgcd(b,a%b,x,y);
ll t=x;x=y;y=t-a/b*y;
}
ll Ni(ll num){
ll x,y; exgcd(num,mod,x,y);
return (x%mod+mod)%mod;
}
void pre(){
p[0]=1;up(i,1,n)p[i]=p[i-1]*10%mod;
up(i,0,n)inv[i]=Ni(p[i]);
}
void getroot(ll x,ll fa){
f[x]=0; size[x]=1;
for(ll i=Link[x];i;i=e[i].next){
if(e[i].y==fa||vis[e[i].y]) continue;
getroot(e[i].y,x); size[x]+=size[e[i].y];
cmax(f[x],size[e[i].y]);
}
cmax(f[x],sum-size[x]);
if(f[x]<f[root]) root=x;
}
void dfs(ll x,ll fa){
ll temp=((mod-dis[x]*inv[deep[x]])%mod+mod)%mod; cnt[temp]++;
for(ll i=Link[x];i;i=e[i].next){
if(vis[e[i].y]||e[i].y==fa) continue;
dis[e[i].y]=(dis[x]*10%mod+e[i].v)%mod;
deep[e[i].y]=deep[x]+1;
dfs(e[i].y,x);
}
}
ll get(ll x,ll fa){
ll temp=cnt[dis[x]%mod];
for(ll i=Link[x];i;i=e[i].next){
if(vis[e[i].y]||e[i].y==fa) continue;
deep[e[i].y]=deep[x]+1;
dis[e[i].y]=(dis[x]+e[i].v*p[deep[x]]%mod)%mod;
temp+=get(e[i].y,x);
}
return temp;
}
ll cal(ll x,ll v,ll depth){
dis[x]=v%mod; deep[x]=depth;
dfs(x,0); cnt[0]--;
return get(x,0);
}
void solve(ll x){
vis[x]=1; cnt.clear(); ans+=cal(x,0,0);
for(ll i=Link[x];i;i=e[i].next){
if(vis[e[i].y]) continue;
cnt.clear();
ans-=cal(e[i].y,e[i].v,1);
root=0; sum=size[e[i].y]; getroot(e[i].y,0);
solve(root);
}
}
int main(){
freopen(FILE".in","r",stdin);
freopen(FILE".out","w",stdout);
n=sum=read(); mod=read(); f[0]=INF; pre();
up(i,1,n-1){ll x=read()+1,y=read()+1,v=read()%mod;insert(x,y,v);insert(y,x,v);}
getroot(1,0); solve(root);
printf("%lld\n",ans);
return 0;
}

本文介绍了一种利用点分治技巧解决特定树形结构中路径计数问题的方法。通过预处理和递归分解,实现了对树中满足特定条件的路径数量的有效计算。
1673

被折叠的 条评论
为什么被折叠?



