2019年ccpc女生赛重现赛题解D

这是2019年ccpc女生赛重现赛题解D。题目为Tree,有三棵树,每个节点有值,有两种操作:对链上节点值开根号向下取整和求链上值的和。给出了输入输出格式及样例,思路是树链剖分,还给出了AC代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

2019年ccpc女生赛重现赛题解D
题目:
Tree
Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 524288/524288 K (Java/Others)
Total Submission(s): 0 Accepted Submission(s): 0

Problem Description
wls 有三棵树,树上每个节点都有一个值 ai,现在有 2 种操作:

  1. 将一条链上的所有节点的值开根号向下取整;
  2. 求一条链上值的和;
    链的定义是两点之间的最短路。

Input
第一行两个数 n, q 分别代表树上点的数量和操作数量。
第二行 n 个整数,第 i 个数代表第 i 个点的值 ai。
接下来 n − 1 行, 每行两个整数 u, v 代表 u,v 之间有一条边。数据保证点两两联通。
接下来 q 行,每行有个整数 op, u, v,op = 0 表示将 u, v 这条链上所有的点的值开根号向下取整,op = 1表示询问 u,v 这条链上的值的和。
1 ≤ n, q ≤ 100, 000
0 ≤ ai ≤ 1, 000, 000, 000

Output
对于每一组 op = 2 的询问,输出一行一个值表示答案。

Sample Input

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

Sample Output

2
4


思路:树链剖分的裸题好像,也是学长敲的。

AC代码:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<string>
#include<cctype>
#include<set>
#include<stack>
#include<map>
#include<queue>
#include<vector>
#include<algorithm>

#define fi first
#define se second
#define ls l,m,rt<<1
#define rs m+1,r,rt<<1|1
#define root 1,n,1
#define PB push_back
#define MP make_pair
#define pi 3.1415926535898
#define MS(x,y) memset(x,y,sizeof(x))
#define lowbit(a)  (a&(-a))

using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<LL,LL> P;
const int maxn = 1e5 + 10,modd = 1e9 + 7,inf = 0x3f3f3f3f,INF = 0x7fffffff,hmod1=0x48E2DCE7,hmod2=0x60000005;
const int dir[4][2] = {{-1,0},{0,-1},{1,0},{0,1}};
const double eps = 1e-8;

template <class T> inline void scand(T &x){char c;x=0;while((c=getchar())<'0');while(c>='0'&&c<='9')x=x*10+(c-48),c=getchar();}
inline LL min(LL a,LL b){return a < b ? a : b;}
inline LL max(LL a,LL b){return a > b ? a : b;}
inline LL gcd(LL a,LL b){ return b==0? a: gcd(b,a%b); }
inline LL exgcd(LL a,LL b,LL &x,LL &y){ LL d; (b==0? (x=1,y=0,d=a): (d=exgcd(b,a%b,y,x),y-=a/b*x)); return d; }
inline LL qpow(LL a,LL n){LL sum=1;while(n){if(n&1)sum=sum*a%modd;a=a*a%modd;n>>=1;}return sum;}
inline LL qmul(LL a,LL n){LL sum=0;while(n){if(n&1)sum=(sum+a)%modd;a=(a+a)%modd;n>>=1;}return sum;}
inline LL inv(LL a) {return qpow(a,modd-2);}
inline LL madd(LL a,LL b){return (a%modd+b%modd)%modd;}
inline LL mmul(LL a,LL b){return a%modd * b%modd;}
inline void uadd(LL &a,LL b){a = madd(a,b);}
inline void umul(LL &a,LL b){a = mmul(a,b);}

struct Edge{int to,next;}edge[2*maxn];
LL n,m,e,tme,v[maxn],head[maxn],dep[maxn],sz[maxn],son[maxn],f[maxn],top[maxn],dfn[maxn],in[maxn];
LL lazy[4*maxn],summ[4*maxn],maxx[4*maxn];

void add_edge(int u,int v)
{
  edge[++e].to = v;
  edge[e].next = head[u];
  head[u] = e;
}

void push_up(int rt)
{
  summ[rt] = summ[rt<<1] + summ[rt<<1|1];
  maxx[rt] = max(maxx[rt<<1],maxx[rt<<1|1]);
}

void build(int l,int r,int rt)
{
  if(l == r)
  {
    summ[rt] = maxx[rt] = dfn[l];
    return;
  }
  int m = (l + r) >> 1;
  build(ls);
  build(rs);
  push_up(rt);
}

void update(int L,int R,int l,int r,int rt)
{
  if(maxx[rt] <= 1) return;
  if(l == r)
  {
    maxx[rt] = sqrt(maxx[rt]);
    summ[rt] = sqrt(summ[rt]);
    return;
  }
  int m = (l + r) >> 1;
  if(L <= m && maxx[rt<<1] > 1) update(L,R,ls);
  if(R > m && maxx[rt<<1|1] > 1)  update(L,R,rs);
  push_up(rt);
}

LL query_summ(int L,int R,int l,int r,int rt)
{
  if(L <= l && r <= R)
    return summ[rt];
  int m = (l + r) >> 1;
  LL ans = 0;
  if(L <= m)  ans += query_summ(L,R,ls);
  if(R > m) ans += query_summ(L,R,rs);
  return ans;
}

void dfs1(int u,int fa)
{
  f[u] = fa,sz[u] = 1;
  LL maxx = -1;
  for(int i=head[u];i;i=edge[i].next)
  {
    int v = edge[i].to;
    if(v == fa) continue;
    dep[v] = dep[u] + 1;
    dfs1(v,u);
    sz[u] += sz[v];
    if(sz[v] > maxx) son[u] = v,maxx = sz[v];
  }
}

void dfs2(int u,int tp)
{
  in[u] = ++tme;
  dfn[tme] = v[u];
  top[u] = tp;  //记录重链顶端
  if(son[u]) dfs2(son[u],tp);
  for(int i=head[u];i;i=edge[i].next)
  {
    int v = edge[i].to;
    if(v == f[u] || v == son[u]) continue;
    dfs2(v,v);  //轻链顶端是自己
  }
}

void uLink(int x,int y) //查询链x --> y
{
  //cout << x << ' ' << y << endl;
  while(top[x] != top[y])
  {
    if(dep[top[x]] < dep[top[y]]) swap(x,y);  //优先处理重链较深的
    update(in[top[x]],in[x],root);
    x = f[top[x]]; //处理完一条重链,跳到上一条
  }
  if(dep[x] > dep[y]) swap(x,y);
  update(in[x],in[y],root);
}

LL qLink(int x,int y) //查询链x --> y
{
  LL ans = 0;
  while(top[x] != top[y])
  {
    if(dep[top[x]] < dep[top[y]]) swap(x,y);  //优先处理重链较深的
    ans += query_summ(in[top[x]],in[x],root);
    x = f[top[x]]; //处理完一条重链,跳到上一条
  }
  if(dep[x] > dep[y]) swap(x,y);
  ans += query_summ(in[x],in[y],root);
  return ans;
}

int main()
{
  int a,b,c;
  scand(n),scand(m);
  for(int i=1;i<=n;i++) scand(v[i]);
  for(int i=0;i<n-1;i++)
  {
    scand(a),scand(b);
    add_edge(a,b);
    add_edge(b,a);
  }
  dfs1(1,-1);dfs2(1,1);
  build(root);
  while(m--)
  {
    scand(a),scand(b),scand(c);
    if(a == 0)  uLink(b,c);
    else if(a == 1) printf("%I64d\n",qLink(b,c));
  }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值