【点分治】Codeforces716E Digit Tree

本文介绍了一种使用点分治算法解决特定问题的方法。通过预处理方案数并暴力搜索,文章详细阐述了如何找到满足特定条件的方案数,包括代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

分析:

说实话,并不知道这题怎么用启发式合并来做。。。总之它就放在启发式合并的练习里面了。。。但我就是用点分治做的。。。

其实这题点分治做起来非常简单。。。

首先先向下找每条到达当前重心的路径的权值,将其设为bibi

然后再每条边依次考虑从重心走下去的权值,将其设为aiai

然后对于这个重心的方案无非就是ai10deepj+bj=0mod pai∗10deepj+bj=0(mod p)的方案数(deepjbjdeepj表示bj这个链的长度)。

然后稍微转化一下,变成ai=bj10deepjai=−bj10deepj
所以预处理一下aiai的方案数,然后再暴力找bibi这一侧就可以了。

#include<cstdio>
#include<cstring>
#include<cmath>
#include<vector>
#include<map>
#define SF scanf
#define PF printf
#define MAXN 100010
using namespace std;
typedef long long ll;
int dp[MAXN];
vector<int>hv[2];
vector<int> a[MAXN];
vector<ll> w[MAXN];
bool vis[MAXN];

int now;
void dfs(int x,int fa){
    dp[x]=1;
    for(int i=0;i<a[x].size();i++){
        int u=a[x][i];
        if(u==fa||vis[u])
            continue;
        dfs(u,x);
        dp[x]+=dp[u];
    }
}
int dfs1(int x,int fa,int siz){
    int minid=-1;
    dp[x]=siz-dp[x];
    for(int i=0;i<a[x].size();i++){
        int u=a[x][i];
        if(u==fa||vis[u])
            continue;
        dp[x]=max(dp[x],dp[u]);
        int v=dfs1(u,x,siz);
        if(minid==-1||dp[v]<dp[minid])
            minid=v;
    }
    if(minid==-1||dp[x]<dp[minid])
        minid=x;
    return minid;
}
int get_hv(int x){
    dfs(x,0);
    return dfs1(x,0,dp[x]);
}
int n;
ll ans,p;
map<ll,int> used;
ll fsp[MAXN],ifsp[MAXN];
void dfs2(int x,int fa,ll pre,int dep){
    ans+=used[(p-pre)*ifsp[dep]%p];
    for(int i=0;i<a[x].size();i++){
        int u=a[x][i];
        if(vis[u]==1||u==fa)
            continue;
        dfs2(u,x,(pre*10ll+w[x][i])%p,dep+1);
    }
}
void dfs3(int x,int fa,ll pre,int dep,int val){
    used[pre]+=val;
    for(int i=0;i<a[x].size();i++){
        int u=a[x][i];
        if(vis[u]==1||u==fa)
            continue;
        dfs3(u,x,(pre+fsp[dep]*w[x][i])%p,dep+1,val);   
    }
}
void solve(int x){
    used.clear();
    used[0]++;
    for(int i=0;i<a[x].size();i++){
        if(vis[a[x][i]])
            continue;
        dfs3(a[x][i],x,w[x][i],1,1);
    }
    for(int i=0;i<a[x].size();i++){
        if(vis[a[x][i]])
            continue;
        dfs3(a[x][i],x,w[x][i],1,-1);
        dfs2(a[x][i],x,w[x][i],1);
        dfs3(a[x][i],x,w[x][i],1,1);    
    }
    ans+=(used[0]-1);
}
void work(){
    while(hv[now].size()){
        for(int i=0;i<hv[now].size();i++){
            int x=hv[now][i];
            solve(x);
            vis[x]=1;
            for(int i=0;i<a[x].size();i++)
                if(vis[a[x][i]]==0)
                    hv[now^1].push_back(get_hv(a[x][i]));
        }
        hv[now].clear();
        now^=1;
    }
}
void exgcd(ll a,ll b,ll &x,ll &y){
    if(b==0){
        x=1;
        y=0;
        return;
    }
    exgcd(b,a%b,x,y);
    ll xx=y;
    ll yy=x-a/b*y;
    x=xx;
    y=yy;
}
ll get_inv(ll x,ll y){
    ll xx,yy;
    exgcd(x,y,xx,yy);
    return (xx%y+y)%y;
} 
int main(){
    SF("%d%d",&n,&p);
    fsp[0]=1;
    for(int i=1;i<=n;i++)
        fsp[i]=fsp[i-1]*10ll%p;
    ifsp[n]=get_inv(fsp[n],p);
    for(int i=n;i>=1;i--)
        ifsp[i-1]=ifsp[i]*10ll%p;
    int u,v,val;
    for(int i=1;i<n;i++){
        SF("%d%d%d",&u,&v,&val);
        val%=p;
        a[u].push_back(v);
        a[v].push_back(u);
        w[u].push_back(val);
        w[v].push_back(val);    
    }
    hv[0].push_back(get_hv(0));
    work();
    PF("%I64d",ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值