[HDU5799]This world need more Zhu

题面戳我
题意:
给定一棵树,m次操作,每次询问某一棵子树中,或者是某一条路径上,出现次数为a的所有数字之和与出现次数为b的所有数字之和的gcd
原题表述:the \(\gcd\) of the sum of numbers that appears \(a\) times and the sum of numbers that appears \(b\) times in subtree \(u\) or on the path from \(u\) to \(v\) .

sol

你把题目看懂了就会发现这是一道莫队傻逼题
首先输入的数据需要离散化
把子树的询问和路径的询问分开处理,然后子树询问可以直接转化为序列莫队。
路径询问就树上莫队即可。

code

记得多组数据初始化
树链剖分中只有son数组要初始化
莫队需要用到的所有数组都要初始化

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
#define ll long long
const int N = 100005;
int gi()
{
    int x=0,w=1;char ch=getchar();
    while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
    if (ch=='-') w=0,ch=getchar();
    while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    return w?x:-x;
}
struct edge{int to,next;}a[N<<1];
int T,n,len,block,BL,m,col[N],o[N],head[N],cnt;
int fa[N],dep[N],sz[N],son[N],top[N],dfn[N],low[N],ref[N];
int ccnt,bl[N],s[N],tp;
int cnt1,cnt2,vis[N],tot[N];ll sum[N],ans[N];
struct query1{
    int l,r,a,b,id;
    bool operator < (const query1 &b) const
        {
            if (l/BL==b.l/BL) return r<b.r;
            return l/BL<b.l/BL;
        }
}q1[N];
struct query2{
    int u,v,a,b,id;
    bool operator < (const query2 &b) const
        {
            if (bl[u]==bl[b.u]) return bl[v]<bl[b.v];
            return bl[u]<bl[b.u];
        }
}q2[N];
void dfs1(int u,int f)
{
    fa[u]=f;dep[u]=dep[f]+1;sz[u]=1;
    for (int e=head[u];e;e=a[e].next)
    {
        int v=a[e].to;if (v==f) continue;
        dfs1(v,u);
        sz[u]+=sz[v];if (sz[v]>sz[son[u]]) son[u]=v;
    }
}
void dfs2(int u,int up)
{
    top[u]=up;dfn[u]=++cnt;ref[cnt]=u;int ttp=tp;
    if (son[u]) dfs2(son[u],up);
    if (tp-ttp>=block) {++ccnt;while (tp>ttp) bl[s[tp--]]=ccnt;}
    for (int e=head[u];e;e=a[e].next)
    {
        int v=a[e].to;if (v==fa[u]||v==son[u]) continue;
        dfs2(v,v);
        if (tp-ttp>=block) {++ccnt;while (tp>ttp) bl[s[tp--]]=ccnt;}
    }
    low[u]=cnt;s[++tp]=u;
}
int lca(int u,int v)
{
    while (top[u]^top[v])
    {
        if (dep[top[u]]<dep[top[v]]) swap(u,v);
        u=fa[top[u]];
    }
    return dep[u]<dep[v]?u:v;
}
void update(int x)
{
    if (!vis[x])
    {
        vis[x]=1;
        sum[tot[col[x]]]-=o[col[x]];
        tot[col[x]]++;
        sum[tot[col[x]]]+=o[col[x]];
    }
    else
    {
        vis[x]=0;
        sum[tot[col[x]]]-=o[col[x]];
        tot[col[x]]--;
        sum[tot[col[x]]]+=o[col[x]];
    }
}
void change(int u,int v)
{
    while (u^v)
        if (dep[u]>dep[v]) update(u),u=fa[u];
        else update(v),v=fa[v];
}
ll Gcd(ll A,ll B){return B?Gcd(B,A%B):A;}
int main()
{
    T=gi();
    for (int zsy=1;zsy<=T;zsy++)
    {
        printf("Case #%d:\n",zsy);
        n=gi();m=gi();block=pow(n,0.6);BL=pow(n,0.5);
        for (int i=1;i<=n;i++) col[i]=o[i]=gi();
        sort(o+1,o+n+1);len=unique(o+1,o+n+1)-o-1;
        for (int i=1;i<=n;i++) col[i]=lower_bound(o+1,o+len+1,col[i])-o;
        memset(head,0,sizeof(head));cnt=ccnt=0;
        memset(son,0,sizeof(son));
        for (int i=1,u,v;i<n;i++)
        {
            u=gi();v=gi();
            a[++cnt]=(edge){v,head[u]};head[u]=cnt;
            a[++cnt]=(edge){u,head[v]};head[v]=cnt;
        }
        dfs1(1,0);cnt=0;dfs2(1,1);
        while (tp) bl[s[tp--]]=ccnt;
        cnt1=cnt2=0;
        for (int i=1,opt,u,v,A,B;i<=m;i++)
        {
            opt=gi();u=gi();v=gi();A=gi();B=gi();
            if (dfn[u]>dfn[v]) swap(u,v);
            if (opt==1) q1[++cnt1]=(query1){dfn[u],low[u],A,B,i};
            else q2[++cnt2]=(query2){u,v,A,B,i};
        }
        sort(q1+1,q1+cnt1+1);sort(q2+1,q2+cnt2+1);
        int L=1,R=0;
        memset(tot,0,sizeof(tot));
        memset(sum,0,sizeof(sum));
        memset(vis,0,sizeof(vis));
        for (int i=1;i<=cnt1;i++)
        {
            while (L>q1[i].l) L--,update(ref[L]);
            while (R<q1[i].r) R++,update(ref[R]);
            while (L<q1[i].l) update(ref[L]),L++;
            while (R>q1[i].r) update(ref[R]),R--;
            ans[q1[i].id]=Gcd(sum[q1[i].a],sum[q1[i].b]);
        }
        memset(tot,0,sizeof(tot));
        memset(sum,0,sizeof(sum));
        memset(vis,0,sizeof(vis));
        change(q2[1].u,q2[1].v);
        int gg=lca(q2[1].u,q2[1].v);
        update(gg);ans[q2[1].id]=Gcd(sum[q2[1].a],sum[q2[1].b]);update(gg);
        for (int i=2;i<=cnt2;i++)
        {
            change(q2[i].u,q2[i-1].u);change(q2[i].v,q2[i-1].v);
            gg=lca(q2[i].u,q2[i].v);
            update(gg);ans[q2[i].id]=Gcd(sum[q2[i].a],sum[q2[i].b]);update(gg);
        }
        for (int i=1;i<=m;i++) printf("%lld\n",ans[i]);
    }
    return 0;
}

转载于:https://www.cnblogs.com/zhoushuyu/p/8318491.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值