1903: #2322. 「清华集训 2017」Hello world!

文章讲述了小V将他的毒瘤题目隐藏在一颗有nnn个节点的树中,并使用魔法使得解题者需进行跳跃操作。ufozgg试图削弱题目,通过跳跃并记录毒瘤值总和。问题要求计算ufozgg进行统计操作时得到的毒瘤值总和。
摘要由CSDN通过智能技术生成

题目描述

不远的一年前,小V还是一名清华集训的选手,坐在机房里为他已如风中残烛的OI生涯做最后的挣扎。而如今,他已成为了一名光荣的出题人。他感到非常激动,不禁感叹道:“Hello world!”。

小V有nnn道题,他的题都非常毒瘤,所以关爱选手的ufozgg打算削弱这些题。为了逃避削弱,小V把他的毒瘤题都藏到了一棵nnn个节点的树里(节点编号从111至nnn),这棵树上的所有节点与小V的所有题一一对应。小V的每一道题都有一个毒瘤值,节点 iii (表示标号为 iii 的树上节点,下同)对应的题的毒瘤值为 aia_iai 。

魔法师小V为了保护他的题目,对这棵树施了魔法,这样一来,任何人想要一探这棵树的究竟,都必须在上面做跳跃操作。每一次跳跃操作包含一个起点 sss 、一个终点 ttt 和一个步频 kkk ,这表示跳跃者会从 sss 出发,在树上沿着简单路径多次跳跃到达 ttt ,每次跳跃,如果从当前点到 ttt 的最短路长度不超过 kkk ,那么跳跃者就会直接跳到 ttt ,否则跳跃者就会沿着最短路跳过恰好 kkk 条边。

既然小V把题藏在了树里,ufozgg就不能直接削弱题目了。他就必须在树上跳跃,边跳跃边削弱题目。ufozgg每次跳跃经过一个节点(包括起点 sss ,当 s=ts=ts=t 的时候也是如此),就会把该节点上的题目的毒瘤值开根并向下取整:即如果他经过了节点iii,他就会使ai=⌊ai⌋a_i=\lfloor{\sqrt{a_i}}\rfloorai=⌊√ai⌋。这种操作我们称为削弱操作。

ufozgg还会不时地希望知道他对题目的削弱程度。因此,他在一些跳跃操作中会放弃对题目的削弱,转而统计该次跳跃经过节点的题目毒瘤值总和。这种操作我们称为统计操作。

吃瓜群众绿绿对小V的毒瘤题和ufozgg的削弱计划常感兴趣。他现在想知道ufozgg每次做统计操作时得到的结果。你能帮帮他吗?

输入

输入的第一行一个正整数 nnn ,表示树的节点数。

接下来一行 nnn 个用空格隔开的正整数 a1,a2,…,an ,依次描述每个节点上题目的毒瘤值。

接下来 n−1n-1n−1 行,描述这棵树。每行 222 个正整数 u,vu,vu,v ,描述一条树上的边 (u,v)\left( u,v\right)(u,v) 。(保证 1≤u,v≤n1\leq u,v\leq n1≤u,v≤n ,保证这 n−1n-1n−1 条边构成了一棵树)

接下来一行一个正整数 QQQ ,表示ufozgg的操作总数。

接下来 QQQ 行按ufozgg执行操作的先后顺序依次描述每个操作,每行 444 个用空格隔开的整数 op,s,t,kop,s,t,kop,s,t,k ,表示ufozgg此次跳跃的起点为 sss ,终点为 ttt ,步频为 kkk 。如果 op=0op=0op=0 ,表示这是一次削弱操作;如果 op=1op=1op=1 ,表示这是一次统计操作。

输出

对于每个统计操作,输出一行一个整数,表示此次统计操作统计到的所有题的毒瘤值总和。

样例输入 

5
1 2 3 4 5
1 2
2 3
3 4
2 5
5
1 1 4 1
1 1 4 2
0 1 5 2
1 2 4 5
1 1 5 1

样例输出 

10
8
6
5

提示

对于100%100\%100%的数据,保证n≤50000n\leq 50000n≤50000,Q≤400000Q\leq 400000Q≤400000,1≤ai≤10131\leq a_i\leq {10}^{13}1≤ai≤1013,对于所有的操作保证0≤op≤10\leq op\leq 10≤op≤1,1≤s,t,k≤n1\leq s,t,k\leq n1≤s,t,k≤n。

测试点编号n=n=n=Q=Q=Q=其他约束测试点分值
1200020002000500050005000121212
2400004000040000400000400000400000141414
3450004500045000400000400000400000141414
4500005000050000400000400000400000对于所有边都有 u+1=vu+1=vu+1=v171717
5500005000050000400000400000400000保证所有初始毒瘤值 ai=1a_i=1ai=1777
6500005000050000400000400000400000保证对于所有询问 k=1k=1k=1131313
7500005000050000400000400000400000232323

 

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<cmath>
using namespace std;
#define ll long long
#define db double
#define For(i,a,b) for(int i=(a);i<=(b);++i)
#define Rep(i,a,b) for(int i=(a);i>=(b);--i)
const int maxn=1e5+7,maxs=200+7,maxt=23,W=19;
ll n,m,a[maxn],s;
  
char cc;ll ff;
template<typename T>void read(T& aa) {
    aa=0;cc=getchar();ff=1;
    while((cc<'0'||cc>'9')&&cc!='-') cc=getchar();
    if(cc=='-') ff=-1,cc=getchar();
    while(cc>='0'&&cc<='9') aa=aa*10+cc-'0',cc=getchar();
    aa*=ff;
}
  
int fir[maxn],nxt[maxn],to[maxn],e=0;
void add(int x,int y) {
    to[++e]=y;nxt[e]=fir[x];fir[x]=e;
    to[++e]=x;nxt[e]=fir[y];fir[y]=e;
}
  
int f[maxn];
int find(int x) {return x==f[x]? x:f[x]=find(f[x]);}
  
int fa[maxn][maxt],jp[maxn][maxs],dep[maxn];
void dfs(int pos,int f) {
    int y,z; dep[pos]=dep[f]+1;
    fa[pos][0]=jp[pos][1]=f; jp[pos][0]=pos;
    For(i,1,W) fa[pos][i]=fa[fa[pos][i-1]][i-1];
    For(i,2,s) jp[pos][i]=jp[f][i-1];
    for(y=fir[pos];y;y=nxt[y]) {
        if((z=to[y])==f) continue;
        dfs(z,pos);
    }
}
  
inline int get_fa(int x,int k) {
    if(k<=s) return jp[x][k];
    Rep(i,W,0) if(k>=(1<<i)) {x=fa[x][i]; k-=(1<<i);}
    return x;
}
  
int get_lca(int x,int y) {
    if(dep[x]!=dep[y]) {
        if(dep[x]<dep[y]) swap(x,y);
        Rep(i,W,0) if(dep[fa[x][i]]>=dep[y]) x=fa[x][i];
    }
    if(x==y) return x;
    Rep(i,W,0) if(fa[x][i]!=fa[y][i]) {x=fa[x][i]; y=fa[y][i];}
    return fa[x][0];
}
  
void chge(int x) {
    if(a[x]==1) return;
    a[x]=floor(sqrt(a[x]));
    if(a[x]==1) f[x]=find(fa[x][0]);
}
  
int q(int x,int y,int lca,int k) {
    if(dep[y]-dep[lca]>=k) return get_fa(y,k);
    k-=dep[y]-dep[lca]; y=lca;
    return get_fa(x,dep[x]-dep[y]-k);
}
  
inline int lst(int x,int k) {
    if(k>s) return get_fa(x,k);
    int y=find(fa[x][0]);
    int p=(dep[x]-dep[y])%k;
    if(p) p=k-p;
    return jp[y][p];
}
  
void get_chge(int x,int y,int k) {
    int lca=get_lca(x,y),len=dep[x]+dep[y]-2*dep[lca];
    if(len%k) chge(y),y=q(x,y,lca,len%k),lca=get_lca(x,y);
    while(dep[x]>=dep[lca]) {chge(x); x=lst(x,k);}
    while(dep[y]>dep[lca]) {chge(y); y=lst(y,k);}
}
  
ll Yth(int x,int y,int k) {
    int lca=get_lca(x,y),len=dep[x]+dep[y]-2*dep[lca]; ll rs=0;
    if(len%k) rs+=a[y],y=q(x,y,lca,len%k),lca=get_lca(x,y);
    rs+=(dep[x]+dep[y]-2*dep[lca])/k+1;
    while(dep[x]>=dep[lca]) {rs+=a[x]-1; x=lst(x,k);}
    while(dep[y]>dep[lca]) {rs+=a[y]-1; y=lst(y,k);}
    return rs;
}
  
int main() {
    read(n); int op,x,y,k;
    s=min(200,(int)sqrt(n)+1);
    For(i,1,n) read(a[i]),f[i]=i;
    For(i,1,n-1) {
        read(x); read(y);
        add(x,y);
    }
    dfs(1,0); read(m);
    For(i,1,n) if(a[i]==1) f[i]=fa[i][0];
    For(i,1,m) {
        read(op); read(x); read(y); read(k);
        if(op==0) get_chge(x,y,k);
        else printf("%lld\n",Yth(x,y,k));
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值