Codeforces372 E. Digit Tree 树分治

来源:互联网 发布:手机赚钱软件 编辑:程序博客网 时间:2024/05/16 12:57
题目链接:http://codeforces.com/contest/716/problem/E

题意:给一棵树,以及一个数m(m<=1e9,且m与10互质),树上的每条边都有一个权值(0到9之间的整数),问有多少点对(u,v)使得u到v路径上的权值组成的数可被m整除。

思路:树分治部分不讲了,就是模板,关键是点权值的记录,记录一条路放在小数点之前的位置的值,以及放在小数点之后的位置的值,进行计数就可以了
自己这样写,竟然被别人嫌弃写的真烂,好好反思了一下,自己当初打算写博客的原因就是因为看不惯有些人的题解写的屁都不是,没想到自己就这样写出了被自己讨厌的题解委屈。我这种凡人,还是很容易懒惰,很容易抱怨周围的事情的吧,不应该

思路:题目要求的其实就是有几个符合条件的(a,b)满足a*10^k+b==0(mod m),(设b的位数为k)一个mod m很容易让我们想到这样转化:a+b/10^k==0(mod m),这样我们就可以事先求好10的各个次方对m的逆元,题目中的m与10互质也验证了这种转换方式的正确性。
只要稍微思考过这道题目,就明白为什么要用逆元来解决这道题,因为树链上的权值可以组成的数很容易求出,但是让两个数两两组合的部分很难解决,如果要一个个组合,统计时的时间复杂度最高可达O(n^2),但是树链上的数,要么作为前面式子中的a,要么是b,作为a的话,就不需要改动,作为b的话,只要乘以10的k次方的逆元就好了。然后用分别存起两个数,用map统计出现次数,就ok了。这样的时间复杂度差不多是O(nlogn2),(把插入map也当做logn, 第二个logn是这样来的)。
#include<cstdio>#include<cstring>#include<iostream>#include<algorithm>#include<map>using namespace std;#define MS(x,y) memset(x,y,sizeof(x))typedef long long LL;const int MAXN=1e5+5;struct Edge{    int nxt,v,w;}edge[MAXN<<1];int head[MAXN],edgenum;void addedge(int u,int v,int w){    edge[edgenum].v=v;    edge[edgenum].w=w;    edge[edgenum].nxt=head[u];    head[u]=edgenum++;}int n,M;LL Div[MAXN],mul[MAXN];map<int,int> SUM;bool used[MAXN];int siz[MAXN],B[MAXN],D[MAXN];int max_sub,temp_root,lim;void dfs(int u,int f,int num){    siz[u]=1;    int K=0,v;    for(int i=head[u];~i;i=edge[i].nxt){        v=edge[i].v;        if(used[v]||v==f) continue;        dfs(v,u,num);        K=max(K,siz[v]);        siz[u]+=siz[v];    }    K=max(K,num-siz[u]);    if(K<max_sub) max_sub=K,temp_root=u;}void dfs(int u,int f,LL b,LL d,int dep){    B[++lim]=b;    D[lim]=d;    int v,w;    for(int i=head[u];~i;i=edge[i].nxt){        v=edge[i].v;        if(used[v]||f==v) continue;        w=edge[i].w;        dfs(v,u,(b+w*mul[dep]%M)%M,(d+w*Div[dep]%M)%M,dep+1);    }}LL work(int root,LL b,LL d,int dep){    LL ret=0;    SUM.clear();    lim=0;    dfs(root,0,b,d,dep);    for(int i=1;i<=lim;++i) SUM[B[i]]++;    for(int i=1;i<=lim;++i) ret+=SUM[(M-D[i])%M];    return ret;}LL work(int root,int num){    max_sub=num;    temp_root=root;    dfs(root,0,num);    root=temp_root;    used[root]=true;    LL ret=work(root,0,0,0);    //cout<<"ROOT: "<<root<<" RET: "<<ret<<endl;    int v,w;    for(int i=head[root];~i;i=edge[i].nxt){        v=edge[i].v;w=edge[i].w;        if(used[v]) continue;        ret-=work(v,w%M,w*Div[0]%M,1);        //cout<<"U: "<<root<<" V: "<<v<<" RET: "<<ret<<endl;        ret+=work(v,siz[v]);    }    //cout<<"root: "<<root<<" ret: "<<ret<<endl;    return ret;}void gcd(LL a,LL b,LL &d,LL &x,LL &y){    if(!b){        d=a;        x=1;        y=0;        return ;    }    gcd(b,a%b,d,y,x);    y-=x*(a/b);}LL inv(LL a,LL n){    LL d,x,y;    gcd(a,n,d,x,y);    return d==1?(x%n+n)%n:-1;}int main(){    while(~scanf("%d%d",&n,&M)){        MS(used,false);        Div[0]=inv(10,M);//        cout<<Div[0]<<endl;        for(int i=1;i<=n;++i) Div[i]=(Div[i-1]*Div[0])%M;        mul[0]=1;mul[1]=10%M;        for(int i=2;i<=n;++i) mul[i]=(mul[i-1]*10)%M;        MS(head,-1);        edgenum=0;        int u,v,w;        for(int i=1;i<n;++i){            scanf("%d%d%d",&u,&v,&w);            ++u;++v;            addedge(u,v,w);            addedge(v,u,w);        }        printf("%I64d\n",work(1,n)-n);    }}


0 0
原创粉丝点击