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
- Codeforces372 E. Digit Tree 树分治
- 【Codeforces 715C&716E】Digit Tree【树分治】
- Codeforces Round #372 (Div. 2) E. Digit Tree (点分治)
- Codeforces Round #372 (Div. 2) E. Digit Tree(点分治,好题)
- CodeForces 715C Digit Tree (树的分治)
- Codeforces 716E E. Digit Tree
- Codeforces Round #372 (Div. 1) C. Digit Tree(树的点分治)
- 【CF#715C】Digit Tree 点分治+乘法逆元
- [Codeforces716E]Digit Tree(点分治+扩欧+二分)
- 【树分治】poj1741 Tree
- 【树分治】poj1741 tree
- POJ1741--Tree(树的分治)
- poj 1741 Tree 树分治
- poj 1741 Tree | 树分治
- poj 1741 Tree 树分治
- 【树分治】 POJ 1741 Tree
- 【POJ 1741】Tree(树分治)
- poj 1741 - Tree 树分治
- c++中,在multimap中查找元素(一键多值)
- PostgreSQL
- C++学习日记3——多态篇的RTTI和异常处理
- 关于Dictionary的TryGetValue方法
- gluPerspective 的聚焦点/zNear/zFar
- Codeforces372 E. Digit Tree 树分治
- 网络爬虫学习笔记(二) 如何开发一个简单的爬虫
- 洛谷 P1122 最大子树和
- MVC框架
- 59.3Sum Closest-最接近的三数之和(中等题)
- Eighty seven (bitset优化) 在n个数中取m个数和为k
- Socket(Tcp)传输在流前面加标识
- 只要情结,不管实用的loT集锦
- 如何用Qt打开Android图像库