行走

“我有个愿望,我希望走到你身边。”
这是个奇异的世界,世界上的n-1条路联结起来形成一棵树,每条路有一个对应的权值ci。
现在我会给出q组询问或操作。
每次询问我会从一个x点走到y点,初始在x点我会有一个数字v,然后每走过一条权值为c的边,我的v就会变成v div c ,问最后到y时v变成了什么。
每次修改我会修改一条边的权值,保证修改后的权值小于等于原来的权值且不会小于1。
每组询问或操作的格式如下:
询问:1 x y v表示从x走到y,一开始的数字为v。
操作:2 p c表示将第p条边的边权修改为c

输入
第一行两个整数n和q表示点个数和询问与操作个数
接下来n-1行每行三个整数u,v,c表示u与v之间有一条边权为c的边
接下来q行每行第一个数type
如果type=1那么接下来三个数x,y,v表示一组询问
如果type=2那么接下来两个数p,c表示一组操作

输出
对于每组询问输出一个数表示最后的答案

样例输入1
6 6
1 2 1
1 3 7
1 4 4
2 5 5
2 6 2
1 4 6 17
2 3 2
1 4 6 17
1 5 5 20
2 4 1
1 5 1 3
样例输出1
2
4
20
3

样例输入2
5 4
1 2 7
1 3 3
3 4 2
3 5 5
1 4 2 100
1 5 4 1
2 2 2
1 1 3 4
样例输出2
2
0
2

对于100%的数据保证 1<=n<=1000001<=ci<=1e18
保证每次修改后的边权小于等于原来的边权且不会小于1

Solution

可以发现,只要不是1的边,走走很快就会变成0,最多只要60次。
我们可以把那些是1的边合并起来,然后只走60次。
虽然这个方法听起来很容易,实现起来还是有些麻烦的。

#include
#include
#include
#include
#include
#define ll long long
using namespace std;
int n,q,x,y,op,tot,p1,p2; 
int head[100005],Next[200005],to[200005];
int deep[100005],fa[100005],f[100005];
ll z,len[200005],s[100005],v1[100],v2[100]; 
void add(int x,int y,ll z)
{
    tot++;
    Next[tot]=head[x];
    to[tot]=y;
    len[tot]=z;
    head[x]=tot;
}
void dfs(int k,int pre)
{
    fa[k]=pre;
    deep[k]=deep[pre]+1;
    for(int i=head[k];i!=-1;i=Next[i]) 
    if(to[i]!=pre) 
    {
        s[to[i]]=len[i]; //s[i]表示其父亲到i的边权
        dfs(to[i],k);
    }
}
int get(int x)
{
    if(f[x]==x) return x; else return f[x]=get(f[x]);
}
int main()
{
    cin>>n>>q;
    for(int i=1;i<=n;i++) head[i]=-1;
    for(int i=1;iscanf("%d%d%lld",&x,&y,&z);
        add(x,y,z);
        add(y,x,z);
    }
    dfs(1,0);fa[1]=1; 
    for(int i=1;i<=n;i++) f[i]=i;
    for(int i=1;i<=n;i++) if(s[i]==1) f[i]=fa[i]; //先合并一下
    for(int i=1;i<=q;i++) 
    {
        scanf("%d",&op);
        if(op==1) 
        {
            scanf("%d%d%lld",&x,&y,&z);
            x=get(x); //待会儿直接把边放进数组,所以要先跳一下
            y=get(y);
            int l1=0,l2=0;
            while(x!=y&&l1+l2<62) 
            {
                if(deep[x]if(y==get(y)) y=get(fa[y]); else y=get(y); //注意如果当前是某个1边权集合的顶端,那你要去fa[y]集合的顶端
                }
                else 
                {
                    l1++;
                    v1[l1]=s[x];
                    if(x==get(x)) x=get(fa[x]); else x=get(x);
                }
            }
            if(l1+l2>=62) printf("0\n");
            else 
            {
                for(int j=1;j<=l1;j++) z=z/v1[j];
                for(int j=1;j<=l2;j++) z=z/v2[j]; 
                printf("%lld\n",z);
            }       
        }
        else 
        {
            scanf("%d%lld",&x,&z);
            p1=to[x*2],p2=to[x*2-1];
            if(deep[p1]//改边权合并的时候要按照树的顺序来
            {
                s[p2]=z;
                if(s[p2]==1) f[p2]=get(fa[p2]); 
            }
            else 
            {
                s[p1]=z;
                if(s[p1]==1) f[p1]=get(fa[p1]);
            }
        }
    }
    return 0;
}

还有一个小问题,x到y的路径,如果x上去有很多1,然后一跳都跳到了LCA的上面,那会不会影响答案呢
因为我的条件是deep[x]与deep[y]比较,所以这时候总是y跳,即使最终跳到了LCA的上方,那也是因为边权1的关系,对最终答案是没影响的。

你可能感兴趣的:(noip2016训练)