BUCT - 2021-2022-1 ACM集训队每周程序设计竞赛(7)题解

本文详细分析了六种不同的树上问题,包括最小值计算、路径距离、最近公共祖先、病毒传播、多彩树距离和边权重修改,分别给出了O(1)、O(n log n)、O(log n)等时间复杂度的解决方案,涉及离线查询、树链剖分、莫队算法和权值线段树等数据结构和算法技巧。
摘要由CSDN通过智能技术生成

A - 神秘的老者

思路
输 出 m i n ( n ∗ a , b ) 即 可 输出min(n* a,b)即可 min(na,b)
时间复杂度 O 1 O1 O1

#include<bits/stdc++.h>
using namespace std;
int main(){
	int n,a,b;
	cin>>n>>a>>b;
	cout<<min(n*a,b)<<endl;
} 

B - 借钱

思路
暴 力 枚 举 即 可 暴力枚举即可
判 断 一 个 浮 点 数 d 是 否 是 整 数 判断一个浮点数d是否是整数 d
f a b s ( d − ( i n t ) d ) < = 1 e − 8 fabs(d-(int)d)<=1e-8 fabs(d(int)d)<=1e8
时间复杂度 O n 2 ∗ d On^2*d On2d

#include <bits/stdc++.h>
#define fer(i,a,b) for(re i = a ; i <= b ; ++ i)
#define der(i,a,b) for(re i = a ; i >= b ; -- i)
#define all(x) (x).begin(),(x).end()
#define sz(x) ((int)(x).size())
#define sf(x) scanf("%lld",&x)
#define pll pair<int,int> 
#define re register int
#define int long long 
#define pb push_back
#define y second 
#define x first 
using namespace std;
const int N = 1e6 + 10 , M = 3010 , mod = 1e9 + 7 ;
const double eps = 1e-7 , pi = acos(-1.0) ;

int n , d ;
int a[M][M] ;

double get(int a[] , int b[]) // 返回a这个点与b这个点的距离
{
    double res = 0 ;
    fer(i,1,d) res = res + (a[i] - b[i]) * (a[i] - b[i]) ;
    return sqrt(res) ;
}

signed main()
{
    cin >> n >> d ;
    
    fer(i,1,n) fer(j,1,d) sf(a[i][j]) ;
    
    int res = 0 ;
    fer(i,1,n)
    {
        fer(j,i + 1 , n)
        {
            double d = get(a[i],a[j]) ;
            if(fabs(d - (int)d) <= 1e-7)
                res ++ ;
        }
    }
    
    cout << res << "\n" ;
    
    return 0;
}

C - 最小值-2019

思路
首 先 由 取 余 的 性 质 得 首先由取余的性质得
( i ∗ j ) % m o d = i % m o d ∗ j % m o d (i * j)\%mod=i\%mod*j\%mod (ij)%mod=i%modj%mod

若 r − l + 1 > = 2019 若r - l + 1 >= 2019 rl+1>=2019
必 定 存 在 一 个 数 x 必定存在一个数x x
使 得 x 属 于 区 间 [ l , r ] 使得x属于区间[l,r] 使x[l,r]
并 且 x % 2019 = 0 并且x\%2019=0 x%2019=0
那 么 当 前 最 小 值 必 定 为 0 那么当前最小值必定为0 0

否 则 考 虑 暴 力 n 2 遍 历 一 遍 即 可 否则考虑暴力n^2遍历一遍即可 n2
时间复杂度 O n 2 On^2 On2

#include <bits/stdc++.h>
#define fer(i,a,b) for(re i = a ; i <= b ; ++ i)
#define der(i,a,b) for(re i = a ; i >= b ; -- i)
#define all(x) (x).begin(),(x).end()
#define sz(x) ((int)(x).size())
#define sf(x) scanf("%lld",&x)
#define pll pair<int,int> 
#define re register int
#define int long long 
#define pb push_back
#define y second 
#define x first 
using namespace std;
const int inf = 0x3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f ;
const int N = 1e6 + 10 , M = 3010 , mod = 1e9 + 7 ;
const double eps = 1e-7 , pi = acos(-1.0) ;

signed main()
{
    int l , r ;
    cin >> l >> r ;
    if((r - l + 1) >= 2019) puts("0") ;
    else
    {
        int res = inf ;
        
        fer(i,l,r)
        {
            fer(j,i + 1 , r)
            {
                res = min(res,i * j % 2019) ;        
            }
        }
        
        cout << res << "\n" ;
    }
    return 0;
}

D - 山 or 水库 ?

思路
假 设 第 i 座 山 的 雨 量 为 b [ i ] 假设第i座山的雨量为b[i] ib[i]
由 题 意 可 得 由题意可得
a [ 1 ] = b [ n ] + b [ 1 ] 2 a[1]=\frac{b[n]+b[1]}{2} a[1]=2b[n]+b[1]
即 即
2 ∗ a [ 1 ] = b [ n ] + b [ 1 ] 2*a[1]=b[n]+b[1] 2a[1]=b[n]+b[1]
2 ∗ a [ 2 ] = b [ 1 ] + b [ 2 ] 2*a[2]=b[1]+b[2] 2a[2]=b[1]+b[2]
2 ∗ a [ 3 ] = b [ 2 ] + b [ 3 ] 2*a[3]=b[2]+b[3] 2a[3]=b[2]+b[3]
. . . . . . . ....... .......
2 ∗ a [ n ] = b [ n − 1 ] + b [ n ] 2*a[n]=b[n-1]+b[n] 2a[n]=b[n1]+b[n]

上 式 累 加 可 得 上式累加可得
2 ∗ ∑ i = 1 n a [ i ] = 2 ∗ ∑ i = 1 n b [ i ] 2*\sum_{i=1}^n a[i]=2*\sum_{i=1}^n b[i] 2i=1na[i]=2i=1nb[i]

即 ∑ i = 1 n a [ i ] = ∑ i = 1 n b [ i ] 即\sum_{i=1}^n a[i]=\sum_{i=1}^n b[i] i=1na[i]=i=1nb[i]

又 因 为 n 是 奇 数 又因为n是奇数 n
上 述 式 子 奇 数 项 相 加 上述式子奇数项相加
2 ∗ a [ 1 ] + 2 ∗ a [ 3 ] + . . . . . . . + 2 ∗ a [ n ] = b [ 1 ] + b [ 2 ] + b [ 3 ] + . . . . . . . . b [ n ] + b [ n ] 2*a[1]+2*a[3]+.......+2*a[n]=b[1]+b[2]+b[3]+........b[n]+b[n] 2a[1]+2a[3]+.......+2a[n]=b[1]+b[2]+b[3]+........b[n]+b[n]

即 2 ∗ ∑ i = 1 2 ∗ i − 1 < = n a [ i ] = ∑ i = 1 n b [ i ] + b [ n ] 即2*\sum_{i=1}^{2*i-1<=n} a[i]=\sum_{i=1}^n b[i]+b[n] 2i=12i1<=na[i]=i=1nb[i]+b[n]

b [ n ] = ∑ i = 1 2 ∗ i − 1 < = n a [ i ] − ∑ i = 1 n b [ i ] = ∑ i = 1 2 ∗ i − 1 < = n a [ i ] − ∑ i = 1 n a [ i ] b[n]=\sum_{i=1}^{2*i-1<=n} a[i]-\sum_{i=1}^n b[i]=\sum_{i=1}^{2*i-1<=n} a[i]-\sum_{i=1}^n a[i] b[n]=i=12i1<=na[i]i=1nb[i]=i=12i1<=na[i]i=1na[i]

其 他 b [ i ] 通 过 b [ n ] 即 可 一 一 推 出 其他b[i]通过b[n]即可一一推出 b[i]b[n]
时间复杂度 O n On On

#include <bits/stdc++.h>
#define fer(i,a,b) for(re i = a ; i <= b ; ++ i)
#define der(i,a,b) for(re i = a ; i >= b ; -- i)
#define all(x) (x).begin(),(x).end()
#define sz(x) ((int)(x).size())
#define sf(x) scanf("%lld",&x)
#define pll pair<int,int> 
#define re register int
#define int long long 
#define pb push_back
#define y second 
#define x first 
using namespace std;
const int N = 1e6 + 10 , M = 3010 , mod = 1e9 + 7 ;
const double eps = 1e-7 , pi = acos(-1.0) ;

int n ;
int a[N] ;
int b[N] ;

signed main()
{
    cin >> n ;
    fer(i,1,n) sf(a[i]) ;
    
    int s1 = 0 , s2 = 0 ;
    fer(i,1,n)
    {
        if(i & 1) s1 += 2 * a[i] ;
        s2 += a[i] ;
    }
    
    b[1] = s1 - s2 ;
    fer(i,1,n-1) b[i + 1] = 2 * a[i] - b[i] ;
    
    fer(i,1,n) cout << b[i] << " " ;
    return 0;
}

E - 病毒树

思路
在这里插入图片描述
画 一 下 图 找 一 下 规 律 即 可 画一下图找一下规律即可
可 以 发 现 若 当 前 节 点 深 度 为 0 答 案 为 k 可以发现若当前节点深度为0答案为k 0k

当 前 节 点 u 深 度 为 1 答 案 为 当前节点u深度为1答案为 u1
∏ i = 1 y = k − s y \prod_{i=1}^{y=k-s} y i=1y=ksy
即 ( k − 1 ) ∗ ( k − 2 ) ∗ . . . . . . ∗ ( k − s ) 即(k-1)*(k-2)*......*(k-s) (k1)(k2)......(ks)
s 为 以 u 为 根 的 下 一 层 儿 子 数 量 s为以u为根的下一层儿子数量 su

当 前 节 点 u 深 度 > = 2 答 案 为 当前节点u深度>=2答案为 u>=2
∏ i = 2 y = k − s y \prod_{i=2}^{y=k-s} y i=2y=ksy
即 ( k − 2 ) ∗ ( k − 3 ) ∗ . . . . . . ∗ ( k − 1 − s ) 即(k-2)*(k-3)*......*(k-1-s) (k2)(k3)......(k1s)
s 为 以 u 为 根 的 下 一 层 儿 子 数 量 s为以u为根的下一层儿子数量 su

根 据 乘 法 原 理 上 述 式 子 累 乘 即 可 根据乘法原理上述式子累乘即可

时间复杂度 O n On On

#include <bits/stdc++.h>
#define fer(i,a,b) for(re i = a ; i <= b ; ++ i)
#define der(i,a,b) for(re i = a ; i >= b ; -- i)
#define all(x) (x).begin(),(x).end()
#define sz(x) ((int)(x).size())
#define sf(x) scanf("%lld",&x)
#define pll pair<int,int> 
#define re register int
#define int long long 
#define pb push_back
#define y second 
#define x first 
using namespace std;
const int inf = 0x3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f ;
const int N = 1e6 + 10 , M = 3010 , mod = 1e9 + 7 ;
const double eps = 1e-7 , pi = acos(-1.0) ;

int n , k ;
vector<int> g[N] ;
int ans ;

int dfs(int u , int fa , int d) // 当前节点u u的父亲节点fa u节点当前深度d
{
    int tep = k - min(d,2ll) ;
    
    for(auto j : g[u])
    {
        if(j == fa) continue ;
        
        if(dfs(j,u,d + 1) == 0) return 0 ;
        
        ans = ans * tep % mod ;
        tep -- ;
    }
    return ans ;
}

signed main()
{
    cin >> n >> k ;
    
    int m = n - 1 ;
    while(m --)
    {
        int a , b ;
        scanf("%d %d",&a,&b) ;
        g[a].pb(b) ;
        g[b].pb(a) ;
    }
    
    ans = k ;
    cout << dfs(1,-1,1) ;
    
    return 0;
}

F - 多彩的树

思路
考 虑 树 上 2 点 u , v 之 间 的 距 离 为 考虑树上2点u,v之间的距离为 2uv
d i s t [ u ] + d i s t [ v ] − 2 ∗ d i s t [ l c a ( u , v ) ] dist[u] + dist[v] - 2* dist[lca(u,v)] dist[u]+dist[v]2dist[lca(u,v)]
d i s t [ i ] 为 根 节 点 到 i 节 点 的 距 离 dist[i]为根节点到i节点的距离 dist[i]i
l c a ( u , v ) 为 u , v 节 点 的 最 近 公 共 祖 先 lca(u,v)为u,v节点的最近公共祖先 lca(u,v)uv
求 2 点 最 近 公 共 祖 先 求2点最近公共祖先 2
在这里插入图片描述
对 每 个 询 问 考 虑 离 线 查 询 对每个询问考虑离线查询 线

设 n u m [ i ] 数 组 为 从 根 节 点 到 u 节 点 颜 色 为 i 的 边 的 数 量 设num[i]数组为从根节点到u节点颜色为i的边的数量 num[i]ui
设 s u m [ i ] 数 组 为 从 根 节 点 到 u 节 点 颜 色 为 i 的 边 的 长 度 总 和 设sum[i]数组为从根节点到u节点颜色为i的边的长度总和 sum[i]ui

将 所 有 颜 色 值 为 c 的 边 的 长 度 改 为 d 将所有颜色值为c的边的长度改为d cd
考 虑 单 次 修 改 对 d i s t [ ] 数 组 的 影 响 考虑单次修改对dist[]数组的影响 dist[]
d i s t [ u ] = d i s t [ u ] − s u m [ c ] + n u m [ c ] ∗ d dist[u]=dist[u]-sum[c]+num[c]*d dist[u]=dist[u]sum[c]+num[c]d

因 此 储 存 所 有 修 改 对 应 的 编 号 i d , 当 前 节 点 u , 当 前 颜 色 x , 当 前 长 度 为 y 因此储存所有修改对应的编号id,当前节点u,当前颜色x,当前长度为y id,u,xy
d i s t [ u ] + d i s t [ v ] − 2 ∗ d i s t [ l c a ( u , v ) ] dist[u]+dist[v]−2∗dist[lca(u,v)] dist[u]+dist[v]2dist[lca(u,v)]
即 可 转 换 为 即可转换为
a n s [ i t . i d ] + = i t . a d d ∗ ( d i s t [ u ] − s u m [ i t . x ] + n u m [ i t . x ] ∗ i t . y ) ans[it.id] += it.add * (dist[u] - sum[it.x] + num[it.x] * it.y) ans[it.id]+=it.add(dist[u]sum[it.x]+num[it.x]it.y)

也 可 以 树 上 莫 队 n s q r t ( n ) 或 者 树 上 建 权 值 线 段 树 n l o g n l o g n 也可以树上莫队nsqrt(n)或者树上建权值线段树nlognlogn nsqrt(n)线nlognlogn

方 法 1 : l c a 离 线 查 询 预 处 理 方法1: lca离线查询预处理 1lca线
时间复杂度 O n l o g n Onlogn Onlogn

#include <bits/stdc++.h>
#define fer(i,a,b) for(re i = a ; i <= b ; ++ i)
#define der(i,a,b) for(re i = a ; i >= b ; -- i)
#define all(x) (x).begin(),(x).end()
#define sz(x) ((int)(x).size())
#define sf(x) scanf("%lld",&x)
#define pll pair<int,int> 
#define re register int
#define int long long 
#define pb push_back
#define y second 
#define x first 
using namespace std;
inline void sf2(int &a , int &b) { sf(a) , sf(b) ;}
const int inf = 0x3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f ;
const int N = 1e6 + 10 , M = 3010 , mod = 1e9 + 7 ;
const double eps = 1e-7 , pi = acos(-1.0) ;

int n , m ;
int e[N] , ne[N] , h[N] , c[N] , w[N] , idx ;// 链式前向星存图的信息
int f[N][20] , d[N] ; // f d数组含义如上图
int q[N] ; // 队列数组
int ans[N] ; // 答案数组
int dist[N] ; // dist[i]为根节点到i节点的距离
int num[N] ; // 从根节点到u节点颜色为i的边的数量
int sum[N] ; // 从根节点到u节点颜色为i的边的长度总和

struct ai{
    int x , y , add , id ;
}; 
// 颜色为x 长度为y 编号为id
// dist[u]+dist[v]−2∗dist[lca(u,v)] add为该式dist[]前面的系数
vector<ai> a[N] ;

void add(int a , int b , int cc , int dd)
{
    e[idx] = b , ne[idx] = h[a] , w[idx] = dd , c[idx] = cc , h[a] = idx ++ ;
}

void bfs() // 预处理d数组和f数组
{
    memset(d,0x3f,sizeof d) ;
    d[0] = 0 , d[1] = 1 ;
    
    int hh = 0 , tt = 0 ;
    q[0] = 1 ;
    
    while(hh <= tt)
    {
        int t = q[hh ++] ;
        
        for(int i = h[t] ; i != -1 ; i = ne[i])
        {
            int j = e[i] ;
            
            if(d[j] > d[t] + 1)
            {
                d[j] = d[t] + 1 ;
                q[++ tt] = j ;
                
                f[j][0] = t ;
                
                for(int k = 1 ; k <= 16 ; k ++)
                    f[j][k] = f[f[j][k-1]][k-1] ;
            }
        }
    }
}

void dfs1(int u , int fa) // 预处理dist数组
{
    for(int i = h[u] ; i != -1 ; i = ne[i])
    {
        int j = e[i] ;
        if(j == fa) continue ;
        
        dist[j] = dist[u] + w[i] ;
        
        dfs1(j,u) ;
    }
}

int lca(int a , int b) // 返回a,b节点的最近公共祖先
{
    if(d[a] < d[b]) swap(a,b) ;
    
    for(int k = 16 ; k >= 0 ; k --)
    {
        if(d[f[a][k]] >= d[b])
        {
            a = f[a][k] ;
        }
    }
    
    if(a == b) return a ;
    
    for(int k = 16 ; k >= 0 ; k --)
    {
        if(f[a][k] != f[b][k])
        {
            a = f[a][k] ;
            b = f[b][k] ;
        }
    }
    
    return f[a][0] ;
}

void dfs(int u , int fa) // 离线查询预处理
{
    for(auto it : a[u])
    {
        ans[it.id] += 1ll * it.add * (dist[u] - sum[it.x] + 1ll * num[it.x] * it.y);
    }
    
    for(int i = h[u] ; i != -1 ; i = ne[i])
    {
        int j = e[i] ;
        
        if(j == fa) continue ;
        
        num[c[i]] ++ ;
        sum[c[i]] += w[i] ;
        
        dfs(j,u) ;
        
        num[c[i]] -- ;
        sum[c[i]] -= w[i] ;
    }
}

signed main()
{
    int n , t ;
    cin >> n >> t ;
    
    memset(h,-1,sizeof h) ;
    
    int m = n - 1 ;
    while(m --)
    {
        int a , b , c , d ;
        sf2(a,b) , sf2(c,d) ;
        add(a,b,c,d) ;
        add(b,a,c,d) ;
    } 
    
    dfs1(1,-1) ;
    bfs() ;
    
    fer(i,1,t)
    {
        int x , y , u , v ;
        sf2(x,y) , sf2(u,v) ;
        
        int fa = lca(u,v) ;
        
        a[u].pb({x,y,1,i}) ;
        a[v].pb({x,y,1,i}) ;
        a[fa].pb({x,y,-2,i}) ;
    }
    
    dfs(1,-1) ;
    
    fer(i,1,t) cout << ans[i] << "\n" ;
    return 0;
}

方 法 2 : 叶 姐 的 树 链 剖 分 + 平 衡 树 方法2: 叶姐的树链剖分+平衡树 2+

#include<iostream>
#include<cstdio>
#include<cstdlib>
using namespace std;
int getint()
{
	char ch;
	do{ch=getchar();}while (ch^'-'&&(ch<'0'||ch>'9'));
	int ans=ch-'0';
	while (isdigit(ch=getchar())) ans=ans*10+ch-'0';
	return ans;
}
int rof[100001],ncnt=0;
struct T
{
	int v,l,r;
	long long sum,num,rnd,x;
}nod[100005];
void update(int x)
{
	nod[x].num=nod[nod[x].l].num+nod[nod[x].r].num+1;
	nod[x].sum=nod[nod[x].l].sum+nod[nod[x].r].sum+nod[x].x;
	return ;
}
int newn(int x,int id)
{
	nod[++ncnt].num=1;
	nod[ncnt].sum=nod[ncnt].x=x,nod[ncnt].v=id;
	nod[ncnt].rnd=rand();
	return ncnt;
}
void split(int dis,int x,int &l,int &r)
{
	if(!dis){l=r=0;return ;}
	if(nod[dis].v<=x)
	{
		l=dis;
		split(nod[dis].r,x,nod[dis].r,r);
	}
	else
	{
		r=dis;
		split(nod[dis].l,x,l,nod[dis].l);
	}
	update(dis);
}
int merge(int l,int r)
{
	if(l==0||r==0)
	  return l+r;
	if(nod[l].rnd<nod[r].rnd)
	{
		nod[l].r=merge(nod[l].r,r);
		update(l);
		return l;
	}
	else
	{
		nod[r].l=merge(l,nod[r].l);
		update(r);
		return r;
	}
}
int n,m,root;
int mod;
struct U
{
	int to,nxt,c,v;
}sid[200001];
int fi[100001],cnt=0,val[100001];
void add()
{
	int x,y,c,v;
	x=getint();y=getint();c=getint();v=getint();
	sid[++cnt].to=y;sid[cnt].nxt=fi[x];fi[x]=cnt;sid[cnt].c=c;sid[cnt].v=v;
	sid[++cnt].to=x;sid[cnt].nxt=fi[y];fi[y]=cnt;sid[cnt].c=c;sid[cnt].v=v;
	return ;
}
int fa[100001],siz[100001],son[100001],dep[100001],fat[100001];
void dfs1(int dis)
{
	dep[dis]=dep[fa[dis]]+1;
	siz[dis]=1;
	for(int i=fi[dis];i;i=sid[i].nxt)
	{
		if(dep[sid[i].to])continue;
		fa[sid[i].to]=dis;
		dfs1(sid[i].to);
		if(siz[dis]<siz[sid[i].to]+1)
		{
			siz[dis]=siz[sid[i].to]+1;
			fat[dis]=sid[i].to;
		}
	}
	return ;
}
long long V[100001];
int id[100001],top[100001],low[100001],num=0; 
void dfs2(int dis,int xx)
{
	if(!dis)
	  return ;
	id[dis]=++num;
	V[num]=sid[xx].v;
	rof[sid[xx].c]=merge(rof[sid[xx].c],newn(sid[xx].v,num));
	top[fat[dis]]=top[dis];
	for(int i=fi[dis];i;i=sid[i].nxt)
	  if(sid[i].to==fat[dis])
		dfs2(fat[dis],i);
	for(int i=fi[dis];i;i=sid[i].nxt)
	{
		if(dis!=fa[sid[i].to]||sid[i].to==fat[dis])
		  continue;
		top[sid[i].to]=sid[i].to;
		dfs2(sid[i].to,i);
	}
	low[dis]=num;
	return ;
}
void chg(int &x,int &y)
{
	int mid;
	mid=x;
	x=y;
	y=mid;
	return ;
}
int ql,qr,lca;
bool LCA(int &x,int &y)
{
	if(x==0||y==0)
	  return false;
	if(top[x]==top[y])
	{
		ql=id[x];qr=id[y];
		x=y=0;
		if(ql>qr)
		  chg(ql,qr);
		ql+=1;
		return true;
	}
	if(dep[top[x]]<dep[top[y]])
	  chg(x,y);
	if(x==top[x])
	{
		ql=qr=id[x];
		x=fa[x];
	}
	else
	{
		ql=id[top[x]];qr=id[x];
		x=fa[top[x]];
	}
	return true;
}
void mak()
{
	dfs1(1);
	top[1]=1;
	dfs2(1,0);
	for(int i=2;i<=n;i++)
	  V[i]+=V[i-1];
}
int main()
{
	n=getint();m=getint();
	for(int i=1;i<n;i++)
	  add();
	mak();
	for(int i(1);i<=m;i++)
	{
		int x,y;
		long long c,v;
		c=getint();v=getint();
		x=getint();y=getint();
		long long ans=0;
		while(LCA(x,y))
		{
			if(ql>qr)break;
			int L,mid,R;
			split(rof[c],ql-1,L,mid);
			split(mid,qr,mid,R);
	//		cout<<ql<<" "<<qr<<"\n";
			ans=ans-nod[mid].sum+v*nod[mid].num-V[ql-1]+V[qr];
			rof[c]=merge(merge(L,mid),R);
		}
		printf("%d\n",ans);
	}
	return 0;
}

方 法 3 : z p f 的 树 链 剖 分 + 权 值 线 段 树 方法3: zpf的树链剖分+权值线段树 3zpf+线

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=100200;
const int M=1e9+20;
struct node {
	int l,r;
	int colornum;
	int sumpath;
}tr[N*40];
int idx=1;
int head[N];//鍙寔涔呭寲绾挎鏍戝ご鎸囬拡
int n,q;

int h[N],to[N*2],ne[N*2],idx2=1;//鍥剧殑瀛樺偍

int depth[N],f[N],sz[N],son[N],top[N],cnt;
int id[N],nw[N],nw2[N];
int w[N],w2[N];
//浠ヤ笂鏄爲閾惧墫鍒嗙殑鍙橀噺
//w琛ㄧず闀垮害锛寃2琛ㄧずcolor

int w3[N*2],w4[N*2];//杈规潈


void dfs( int rt,int fa){//灏嗚竟鏉冭浆鍖栦负鐐规潈
	for( int i=h[rt];i!=-1;i=ne[i]){
		int j=to[i];
		if(j==fa) continue;
		w2[j]=w3[i];
		w[j]=w4[i];
		dfs(j,rt);
	}
}
void add( int a,int b,int c,int d){
	w4[idx2]=d,w3[idx2]=c,to[idx2]=b,ne[idx2]=h[a],h[a]=idx2++;
}


void pushup( int rt){
	tr[rt].sumpath=tr[tr[rt].l].sumpath+tr[tr[rt].r].sumpath;
	tr[rt].colornum=tr[tr[rt].l].colornum+tr[tr[rt].r].colornum;
}
int create( int l,int r){
	int p=idx++;
	if(l==r){
		tr[p].sumpath=nw[l];
		return p;
	}
	int mid=(l+r)/2;
	tr[p].l=create(l,mid);
	tr[p].r=create(mid+1,r);
	tr[p].sumpath=tr[tr[p].l].sumpath+tr[tr[p].r].sumpath;
	return p;
}
int insert(int o,int l,int r,int val,int val_path){
	int rt=idx++;
	tr[rt]=tr[o];
	if(l==r){
		tr[rt].colornum+=1;
		tr[rt].sumpath+=val_path;
		return rt;
	}
	int mid=(l+r)/2;
	if(val<=mid){
		tr[rt].l=insert(tr[rt].l,l,mid,val,val_path);
	}	
	else tr[rt].r=insert(tr[rt].r,mid+1,r,val,val_path);
	pushup(rt);
	return rt;
}
int q_color( int rl,int rr,int l,int r,int L,int R){
	if(l>R||r<L) return 0;
	if(l==r) return tr[rr].colornum-tr[rl].colornum;
	if(l>=L&&r<=R) return tr[rr].colornum-tr[rl].colornum;
	int mid=(l+r)/2;
	int res=0;
	if(mid>=L) res+=q_color(tr[rl].l,tr[rr].l,l,mid,L,R);
	if(R>mid) res+=q_color(tr[rl].r,tr[rr].r,mid+1,r,L,R);
	return res;
}
int q_path( int rl,int rr,int l,int r,int L,int R){
	if(l>R||r<L) return 0;
	if(l==r) return tr[rr].sumpath-tr[rl].sumpath;
	if(l>=L&&r<=R) return tr[rr].sumpath-tr[rl].sumpath;
	int mid=(l+r)/2;
	int res=0;
	if(mid>=L) res+=q_path(tr[rl].l,tr[rr].l,l,mid,L,R);
	if(R>mid) res+=q_path(tr[rl].r,tr[rr].r,mid+1,r,L,R);
	return res;	
}
//浠ヤ笂鏄彲鎸佷箙鍖栫嚎娈垫爲鎿嶄綔

void dfs1( int rt,int fa){
	depth[rt]=depth[fa]+1;f[rt]=fa;
	sz[rt]=1;son[rt]=0;
	for( int i=h[rt];i!=-1;i=ne[i]){
		int j=to[i];
		if(j==fa) continue;
		else dfs1(j,rt);
		sz[rt]+=sz[j];
		if(sz[son[rt]]<sz[j])
			son[rt]=j;
		
	}
}
void dfs2( int rt,int fa){//鍏堝簭閬嶅巻dfs 
	id[rt]=++cnt;
	nw[id[rt]]=w[rt];
	nw2[id[rt]]=w2[rt];
	//杩欓噷w鏄偣鏉冿紝涓嶆槸杈规潈锛宯w鏁扮粍鏄皢鏍戝睍寮€鎴愰摼寮忕粨鏋勫悗姣忎釜鐐圭殑鏉冨€?
	if(son[fa]==rt) top[rt]=top[fa];
	else top[rt]=rt;
	if(!son[rt]) return ;
	dfs2(son[rt],rt);//棣栧厛閬嶅巻閲嶅効瀛?
	for( int i=h[rt];i!=-1;i=ne[i]){
		int j=to[i];
		if(j==fa||j==son[rt]) continue;
		else dfs2(j,rt);
	}
} 

int query_color( int u,int v,int c){
	int res=0;
    while(top[u] !=top[v] ){//uv涓嶅湪鍚屼竴鏉¢摼涓?
            //淇濊瘉v鍦╱鐨勪笂闈?
        if(depth[top[u]]<depth[top[v]]) swap(u,v);
            //鏇存柊鏍戦摼
        res+=q_color(head[id[top[u]]-1],head[id[u]],1,n,c,c);
        u=f[top[u]];
    }
    if(depth[u]<depth[v]) swap(u,v);
    res+=q_color(head[id[v]],head[id[u]],1,n,c,c);
    return res;
}
int query_path_val( int u,int v,int c){
	int res=0;
    while(top[u] !=top[v] ){//uv涓嶅湪鍚屼竴鏉¢摼涓?
            //淇濊瘉v鍦╱鐨勪笂闈?
        if(depth[top[u]]<depth[top[v]]) swap(u,v);
            //鏇存柊鏍戦摼
        res+=q_path(head[id[top[u]]-1],head[id[u]],1,n,c,c);
		// cout<<id[top[u]]<<id[u]<<u<<top[u]<<endl;
        u=f[top[u]];
    }
    if(depth[u]<depth[v]) swap(u,v);
    res+=q_path(head[id[v]],head[id[u]],1,n,c,c);
    return res;
}
int query_path_val_sum( int u,int v){
	int res=0;
    while(top[u] !=top[v] ){//uv涓嶅湪鍚屼竴鏉¢摼涓?
            //淇濊瘉v鍦╱鐨勪笂闈?
        if(depth[top[u]]<depth[top[v]]) swap(u,v);
        res+=q_path(0,head[0],1,n,id[top[u]],id[u]);
        u=f[top[u]];
    }
    if(depth[u]<depth[v]) swap(u,v);
    res+=q_path(0,head[0],1,n,id[v]+1,id[u]);
    return res;
}


int main(){
	memset(h,-1,sizeof(h));
	cin>>n>>q;
	for( int i=1;i<n;i++){
		int a,b,c,d;
		scanf("%d%d%d%d",&a,&b,&c,&d);
		add(a,b,c,d);
		add(b,a,c,d);
	}
	dfs(1,0);
	dfs1(1,0);
	dfs2(1,0);
	head[0]=create(1,n);
	int cnt_head=1;
	for( int i=2;i<=n;i++){
		head[i]=insert(head[i-1],1,n,nw2[i],nw[i]);
	}
	for( int i=1;i<=q;i++){
		int x,y,u,v;
		scanf("%d%d%d%d",&x,&y,&u,&v);
		int res=query_path_val_sum(u,v)-query_path_val(u,v,x)+query_color(u,v,x)*y;
		printf("%d\n",res);
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值