【点分治】【乘法逆元】hdu4812 D Tree

思路比较裸,但是要把答案存到哈希表里面,这里需要一定技巧,否则会被K=1且点权全是1的数据卡飞。预处理乘法逆元。TLE了一天。换了种点分治的姿势……

#pragma comment(linker,"/STACK:102400000,102400000")
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int Res;char C;
inline int Read()
{
    Res=0;C=‘*‘;
    while(C<‘0‘||C>‘9‘)C=getchar();
    while(C>=‘0‘&&C<=‘9‘){Res=Res*10+(C-‘0‘);C=getchar();}
    return Res;
}
#define MAXN 100001
#define MOD 1000003
#define INF 2147483647
typedef long long ll;
typedef pair<int,int> Point;
typedef pair<ll,int> Point2;
int n,K;
Point ans;
ll m,w[MAXN],inv[MOD];
int v[MAXN<<1],first[MAXN],next[MAXN<<1],en,id[MOD];
void AddEdge(const int &U,const int &V)
{
    v[++en]=V;
    next[en]=first[U];
    first[U]=en;
}
bool centroid[MAXN];
int size[MAXN];
int calc_sizes(int U,int Fa)
{
    int res=1;
    for(int i=first[U];i;i=next[i])
      if(v[i]!=Fa&&(!centroid[v[i]]))
        res+=calc_sizes(v[i],U);
    return size[U]=res;
}
Point calc_centroid(int U,int Fa,int nn)
{
    Point res=make_pair(INF,-1);
    int sum=1,maxv=0;
    for(int i=first[U];i;i=next[i])
      if(v[i]!=Fa&&(!centroid[v[i]]))
        {
          res=min(res,calc_centroid(v[i],U,nn));
          maxv=max(maxv,size[v[i]]);
          sum+=size[v[i]];
        }
    maxv=max(maxv,nn-sum);
    res=min(res,make_pair(maxv,U));
    return res;
}
int En,last;
Point2 dis[MAXN];
void calc_dis(int root,int U,int Fa,ll d)
{
    dis[En++]=make_pair(d,U);
    for(int i=first[U];i;i=next[i])
      if(v[i]!=Fa&&(!centroid[v[i]]))
        calc_dis(root,v[i],U,d*w[v[i]]%MOD);
}
void calc_pairs(int s)
{
    for(int i=last;i<En;++i)
      {
        ll t=m*inv[dis[i].first*w[s]%MOD]%MOD;
        if(id[t])
          ans=min(ans,dis[i].second<id[t]?
          make_pair(dis[i].second,id[t]):
          make_pair(id[t],dis[i].second));
      }
    for(int i=last;i<En;++i)
      {
      	if(id[dis[i].first]) id[dis[i].first]=min(id[dis[i].first],dis[i].second);
      	else id[dis[i].first]=dis[i].second;
      }
}
void solve(int U)
{
    calc_sizes(U,-1);
    int s=calc_centroid(U,-1,size[U]).second;
    centroid[s]=1;
    for(int i=first[s];i;i=next[i])
      if(!centroid[v[i]])
        solve(v[i]);
    En=0; dis[En++]=make_pair(1,s); id[1]=s;
    for(int i=first[s];i;i=next[i])
      if(!centroid[v[i]])
        {
          last=En;
          calc_dis(v[i],v[i],s,w[v[i]]);
          calc_pairs(s);
        }
    for(int i=0;i<En;++i) id[dis[i].first]=0;
    centroid[s]=0;
}
void init()
{
    memset(first,0,(n+1)*sizeof(int));
    en=0; ans=make_pair(INF,INF);
}
int main()
{
    inv[1]=1;
    for(int i=2;i<MOD;i++)
      {
        int a=MOD/i,b=MOD%i;
        inv[i]=(inv[b]*(-a)%MOD+MOD)%MOD;
      }
    int a,b;
    while(scanf("%d%I64d",&n,&m)!=EOF)
      {
        init();
        for(int i=1;i<=n;++i)
          {
            w[i]=(ll)Read();
            w[i]%=MOD;
          }
        for(int i=1;i<n;++i)
          {
            a=Read(); b=Read();
            AddEdge(a,b);
            AddEdge(b,a);
          }
        solve(1);
        if(ans==make_pair(INF,INF))
          puts("No solution");
        else
          printf("%d %d\n",ans.first,ans.second);
      }
    return 0;
}
时间: 2024-10-10 11:28:53

【点分治】【乘法逆元】hdu4812 D Tree的相关文章

HDU4812 D Tree(树的点分治)

题目大概说给一棵有点权的树,输出字典序最小的点对,使这两点间路径上点权的乘积模1000003的结果为k. 树的点分治搞了.因为是点权过根的两条路径的LCA会被重复统计,而注意到1000003是质数,所以这个用乘法逆元搞一下就OK了.还有要注意“治”的各个实现,把时间复杂度“控制”在O(nlogn). WA了几次,WA在漏了点到子树根的路径,还有每次分治忘了清空数组. 1 #include<cstdio> 2 #include<cstring> 3 #include<algor

BZOJ 3813 奇数国 欧拉函数+线段树+乘法逆元

题目大意:给出一个序列,支持修改操作,求这个序列连续一段的乘积的欧拉函数.每个数的最大质因子不超过281. 思路:φ(n) = n * (1 - 1 / p1) * (1 - 1 / p2) * (1 - 1 / p3) * (1 - 1 / p4)--*(1 - 1 / pn) = n  / (p1 * p2 * p3 * -- * pn) * ((p1 - 1) * (p2 - 1) * (p3 - 1) * -- * (pn - 1)) 于是这个东西只需要维护一下区间中的指数,用bitse

HDU3037 Saving Beans(Lucas定理+乘法逆元)

题目大概问小于等于m个的物品放到n个地方有几种方法. 即解这个n元一次方程的非负整数解的个数$x_1+x_2+x_3+\dots+x_n=y$,其中0<=y<=m. 这个方程的非负整数解个数是个经典问题,可以+1转化正整数解的个数用插板法解决:$C_{y+n-1}^{n-1}=C_{y+n-1}^y$. 而0<=y<=m,最后的结果就是—— $$\sum_{i=0}^m C_{i+n-1}^i$$ $$C_{n-1}^0+C_{n}^1+C_{n+1}^2+\dots+C_{n-1

UVa 11174 (乘法逆元) Stand in a Line

题意: 有n个人排队,要求每个人不能排在自己父亲的前面(如果有的话),求所有的排队方案数模1e9+7的值. 分析: <训练指南>上分析得挺清楚的,把公式贴一下吧: 设f(i)为以i为根节点的子树的排列方法,s(i)表示以i为根的子树的节点总数. f(i) = f(c1)f(c2)...f(ck)×(s(i)-1)!/(s(c1)!s(c2)!...s(ck)!) 按照书上最开始举的例子,其实这个式子也不难理解,就是先给这些子树确定一下位置,即有重元素的全排列. 子树的位置确定好以后,然后再确定

LightOJ - 1050 (唯一分解+推公式+乘法逆元)

题意:求a^b的所有约数和对1e9+7取模的结果 思路:对于一个数p,进行唯一分解,则p=P1^M1*P2^M2*...*Pn^Mn,则p的所有约数之和等于(P1^0+P1^1+...+P1^M1)*(P2^0+P2^1+...+P2^M2)*...*(Pn^0+Pn^1+...+Pn^Mn), p^t=P1^(M1*t)*P2^(M2*t)*...*Pn^(Mn*t),每一个(Pn^0+Pn^1+...+Pn^Mn)利用等比数列可以直接推出公式为(Pn^(Mn*t+1)-1)/(Pn-1),用

【奇技淫巧】数学技巧之乘法逆元

一.写在前面 开始码这篇blog之前我就意识到,这篇blog将会是我到目前为止码出的所有blog中最水的一篇.说是讲乘法逆元,但蒟蒻博主自己都不会证明_(:з」∠)_所以只打算放一个说明书式的用法,还请诸位看官老爷轻喷. 二.关于乘法逆元 我们知道模法交配率(Magic Coitus Law)(←_←其实并没有这东西)并不适用于除法.用式子表示大概是这样的: 但乘法逆元(Multiplicative Inverse Modulo,下式中用a'表示)就可以完成这样一个奇妙的操作: 然后我们就能用模

乘法逆元...Orz

最近打的几场比赛,都出现了有关逆元的题目,今天就整理了一下... 求乘法逆元的几种方法:http://www.cnblogs.com/james47/p/3871782.html 博文转载链接:http://blog.csdn.net/acdreamers/article/details/8220787 今天我们来探讨逆元在ACM-ICPC竞赛中的应用,逆元是一个很重要的概念,必须学会使用它. 对于正整数和,如果有,那么把这个同余方程中的最小正整数解叫做模的逆元. 逆元一般用扩展欧几里得算法来求

Codeforces 543D Road Improvement(树形DP+乘法逆元)

题目大概说给一棵树,树的边一开始都是损坏的,要修复一些边,修复完后要满足各个点到根的路径上最多只有一条坏的边,现在以各个点为根分别求出修复边的方案数,其结果模1000000007. 不难联想到这题和HDU2196是一种类型的树形DP,因为它们都要分别求各个点的答案.然后解法也不难想: dp0[u]表示只考虑以u结点为根的子树的方案数 dp1[u]表示u结点往上走,倒过来,以它父亲为根那部分的方案数 有了这两部分的结果,对于各个点u的答案就是dp0[u]*(dp1[u]+1).这两部分求法如下,画

乘法逆元(转)

定义:满足a*k≡1 (mod p)的k值就是a关于p的乘法逆元. 为什么要有乘法逆元呢? 当我们要求(a/b) mod p的值,且a很大,无法直接求得a/b的值时,我们就要用到乘法逆元. 我们可以通过求b关于p的乘法逆元k,将a乘上k再模p,即(a*k) mod p.其结果与(a/b) mod p等价. 证: 根据b*k≡1 (mod p)有b*k=p*x+1. k=(p*x+1)/b. 把k代入(a*k) mod p,得:(a*(p*x+1)/b) mod p