codeforces 593D Happy Tree Party Lca+并查集

题意:
给定一棵无根树,每条边有边权。
两种操作。
第一种询问给定值 y(<=1018) 除以点u到点v的所有边的边权的值,注意每一次除都是下取整。
第二种是修改某边边权,并且每一次修改后该边边权相对原来一定减小。
边权为正整数 (<=1018)
n<=2105 ,询问次数 m<=2105


解析:
sb cin害我T了好几发。
首先 (a/b)/c 是否等于 a/(bc) ,除法都是下取整。
反正我暴力拍1~1000没拍出来不等于的。
那暂且认为这是对的。
如果没有操作2
我们显然可以利用倍增来强行搞这道题。
mul[x][i] 表示 x fa[x][i]路径上所有边的乘积。
由于会爆 longlong 所以可以考虑转成 double 型。
总复杂度 O(nlogn+mlogn)
那么如果有操作2呢。
显然暴力重构 mul 数组会T成渣渣。
这个方法直接GG。
所以需要换一个思路。
我们发现 1018 至多除以60个2之后就变成了0。
这说明如果边权全部大于1的话。
我们只需要找到 u,v lca ,之后暴力向上除就可以了,如果除了60次以上或者没到60次val就已经变成0的话,那么直接返回0即可。
那么修改呢?直接改边就可以了。
如果边权有等于1的怎么办。
因为图是一棵树,所以显然我们可以路径压缩边权为1的边。
查询向上搜的时候直接找祖先即可。
那么有修改呢?
因为修改一定是把边减小,所以如果减成1了,那么继续压缩一下路径,否则直接修改。
所以这题就可以在O(并查集*m*logn)内解决了。


那么如果修改操作不限定一定减小边权呢?
抱歉我不会。
我也没想出来好方法。


#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 200100
#define eps 1e-6
using namespace std;
typedef long long ll;
int n,m;
int head[N],cnt;
struct Line
{
    int u,v;
    ll val;
}l[N];
struct node
{
    int from,to,next;
    ll val;
}edge[N<<1];
void init()
{
    memset(head,-1,sizeof(head));
    cnt=1;
}
int ancestor[N];
int find(int x)
{
    if(x==ancestor[x])return x;
    return ancestor[x]=find(ancestor[x]);
}
void edgeadd(int from,int to,ll val)
{
    edge[cnt].from=from,edge[cnt].to=to,edge[cnt].val=val;
    edge[cnt].next=head[from];head[from]=cnt++;
}
int deep[N],fa[N][21];
ll col[N];
void dfs(int now,int ff)
{
    deep[now]=deep[ff]+1;
    fa[now][0]=ff;
    for(int i=head[now];i!=-1;i=edge[i].next)
    {
        int to=edge[i].to;
        if(to==ff)continue;
        dfs(to,now);
        col[to]=edge[i].val;
        if(edge[i].val==1)
        {
            int fx=find(to),fy=find(now);
            ancestor[fx]=fy;
        }
    }
}
int lca(int x,int y)
{
    if(deep[x]<deep[y])swap(x,y);
    for(int i=20;i>=0;i--)
        if(deep[fa[x][i]]>=deep[y])
            x=fa[x][i];
    if(x==y)return x;
    for(int i=20;i>=0;i--)
        if(deep[fa[x][i]]==deep[fa[y][i]]&&fa[x][i]!=fa[y][i])
            x=fa[x][i],y=fa[y][i];
    return fa[x][0]; 
}
ll calc(int u,int v,int Lca,ll val)
{
    int ccnt=0;
    while(deep[u]>deep[Lca])
    {
        int anc=find(u);
        if(deep[anc]<=deep[Lca])break;
        val/=col[anc];
        ccnt++;
        if(ccnt>64)return 0;
        if(val==0)return 0;
        u=fa[anc][0];
    }
    while(deep[v]>deep[Lca])
    {
        int anc=find(v);
        if(deep[anc]<=deep[Lca])break;
        val/=col[anc];
        if(ccnt>64)return 0;
        if(val==0)return 0;
        v=fa[anc][0];
    }
    return val;
}
void change(int no,ll val)
{
    int x=l[no].u,y=l[no].v;
    if(fa[y][0]==x)swap(x,y);
    col[x]=val;
    if(val==1)
    {
        int fx=find(x),fy=find(y);
        ancestor[fx]=fy;
    }
}
int main()
{
    init();
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)ancestor[i]=i;
    for(int i=1;i<n;i++)
    {
        scanf("%d%d%I64d",&l[i].u,&l[i].v,&l[i].val); 
        edgeadd(l[i].u,l[i].v,l[i].val);
        edgeadd(l[i].v,l[i].u,l[i].val);
    }
    dfs(1,0);
    for(int i=1;i<=20;i++)
        for(int j=1;j<=n;j++)
            fa[j][i]=fa[fa[j][i-1]][i-1];
    for(int i=1;i<=m;i++)
    {
        int opt;
        scanf("%d",&opt);
        if(opt==1)
        {
            int u,v;
            ll val;
            scanf("%d%d%I64d",&u,&v,&val);
            int tmp=lca(u,v);
            printf("%I64d\n",calc(u,v,tmp,val));
        }else
        {
            int x;
            ll val;
            scanf("%d%I64d",&x,&val);
            change(x,val);
        }
    }
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值