“我有个愿望,我希望走到你身边。”
这是个奇异的世界,世界上的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
可以发现,只要不是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的关系,对最终答案是没影响的。