洛谷-【图论2-1】树2

P3128 [USACO15DEC] Max Flow P

题目描述

Farmer John 在他的谷仓中安装了 N−1 条管道,用于在 N 个牛棚之间运输牛奶(2≤N≤50,000),牛棚方便地编号为 1...N。每条管道连接一对牛棚,所有牛棚通过这些管道相互连接。

FJ 正在 K 对牛棚之间泵送牛奶(1≤K≤100,000)。对于第 i 对牛棚,你被告知两个牛棚 si​ 和 ti​,这是牛奶以单位速率泵送的路径的端点。FJ 担心某些牛棚可能会因为过多的牛奶通过它们而不堪重负,因为一个牛棚可能会作为许多泵送路径的中转站。请帮助他确定通过任何一个牛棚的最大牛奶量。如果牛奶沿着从 si​ 到 ti​ 的路径泵送,那么它将被计入端点牛棚 si​ 和 ti​,以及它们之间路径上的所有牛棚。

输入格式

输入的第一行包含 N 和 K。

接下来的 N−1 行每行包含两个整数 x 和 y(x=y),描述连接牛棚 x 和 y 的管道。

接下来的 K 行每行包含两个整数 s 和 t,描述牛奶泵送路径的端点牛棚。

输出格式

输出一个整数,表示通过谷仓中任何一个牛棚的最大牛奶量。

输入输出样例

输入 #1复制

复制代码
5 10
3 4
1 5
4 2
5 4
5 4
5 4
3 5
4 3
4 3
1 3
3 5
5 4
1 5
3 4

输出 #1复制

复制代码
9

说明/提示

2≤N≤5×104,1≤K≤105。

实现代码:

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;
#define maxn 50010
#define ll long long
#define res register int
struct Node{
	int to,next;
};
Node edge[maxn<<2]; 
int head[maxn<<2],power[maxn],n,m,d[maxn],fa[maxn][30],ans,num;

inline int read(){ 
	int s=0;
	char c=getchar();
	while (c<'0' || c>'9') c=getchar();
	while (c>='0' && c<='9') s=s*10+c-'0',c=getchar();
	return s;
}
inline void add(int x,int y){edge[++num].to=y,edge[num].next=head[x],head[x]=num;}
inline void work(int u,int fath){
	d[u]=d[fath]+1,fa[u][0]=fath;
	for (res i=0;fa[u][i];++i) fa[u][i+1]=fa[fa[u][i]][i];
	for (res i=head[u];i;i=edge[i].next){
		int e=edge[i].to;
		if (e!=fath) work(e,u);
	}
}
inline int Lca(int u,int v){
	if (d[u]>d[v]) swap(u,v);
	for (res i=20;i>=0;--i) if (d[u]<=d[v]-(1<<i)) v=fa[v][i];
	if (u==v) return u;
	for (res i=20;i>=0;--i) if (fa[u][i]!=fa[v][i]) u=fa[u][i],v=fa[v][i];
	return fa[u][0];
}
inline void Get(int u,int fath){
	for (res i=head[u];i;i=edge[i].next){
		int e=edge[i].to;
		if (e==fath) continue;
		Get(e,u);
		power[u]+=power[e];
	}
	ans=max(ans,power[u]);
}

int main(){
	n=read(),m=read();
	int x,y;
	for (res i=1;i<n;++i){
		x=read(),y=read();
		add(x,y); add(y,x);
	}
	work(1,0);
	for (res i=1; i<=m; ++i){
		x=read(),y=read();
		int lca=Lca(x,y);
		++power[x];++power[y];--power[lca];--power[fa[lca][0]]; 
	}
	Get(1,0);
	printf("%d\n",ans);
	return 0;
}

P3384 【模板】重链剖分 / 树链剖分

题目描述

如题,已知一棵包含 N 个结点的树(连通且无环),每个节点上包含一个数值,需要支持以下操作:

  • 1 x y z,表示将树从 x 到 y 结点最短路径上所有节点的值都加上 z。

  • 2 x y,表示求树从 x 到 y 结点最短路径上所有节点的值之和。

  • 3 x z,表示将以 x 为根节点的子树内所有节点值都加上 z。

  • 4 x,表示求以 x 为根节点的子树内所有节点值之和。

输入格式

第一行包含 4 个正整数 N,M,R,P,分别表示树的结点个数、操作个数、根节点序号和取模数(即所有的输出结果均对此取模)。

接下来一行包含 N 个非负整数,分别依次表示各个节点上初始的数值。

接下来 N−1 行每行包含两个整数 x,y,表示点 x 和点 y 之间连有一条边(保证无环且连通)。

接下来 M 行每行包含若干个正整数,每行表示一个操作。

输出格式

输出包含若干行,分别依次表示每个操作 2 或操作 4 所得的结果(对 P 取模)。

输入输出样例

输入 #1复制

复制代码
5 5 2 24
7 3 7 8 0 
1 2
1 5
3 1
4 1
3 4 2
3 2 2
4 5
1 5 1 3
2 1 3

输出 #1复制

复制代码
2
21

说明/提示

【数据规模】

对于 30% 的数据: 1≤N≤10,1≤M≤10;

对于 70% 的数据: 1≤N≤103,1≤M≤103;

对于 100% 的数据: 1≤N≤105,1≤M≤105,1≤R≤N,1≤P≤230。所有输入的数均在 int 范围内。

【样例说明】

树的结构如下:

各个操作如下:

故输出应依次为 2 和 21。

实现代码:

cpp 复制代码
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#define Rint register int
#define mem(a,b) memset(a,(b),sizeof(a))
#define Temp template<typename T>
using namespace std;
typedef long long LL;
Temp inline void read(T &x){
    x=0;T w=1,ch=getchar();
    while(!isdigit(ch)&&ch!='-')ch=getchar();
    if(ch=='-')w=-1,ch=getchar();
    while(isdigit(ch))x=(x<<3)+(x<<1)+(ch^'0'),ch=getchar();
    x=x*w;
}

#define mid ((l+r)>>1)
#define lson rt<<1,l,mid
#define rson rt<<1|1,mid+1,r
#define len (r-l+1)

const int maxn=200000+10;
int n,m,r,mod;
int e,beg[maxn],nex[maxn],to[maxn],w[maxn],wt[maxn];
int a[maxn<<2],laz[maxn<<2];
int son[maxn],id[maxn],fa[maxn],cnt,dep[maxn],siz[maxn],top[maxn]; 
int res=0;

inline void add(int x,int y){
    to[++e]=y;
    nex[e]=beg[x];
    beg[x]=e;
}
inline void pushdown(int rt,int lenn){
    laz[rt<<1]+=laz[rt];
    laz[rt<<1|1]+=laz[rt];
    a[rt<<1]+=laz[rt]*(lenn-(lenn>>1));
    a[rt<<1|1]+=laz[rt]*(lenn>>1);
    a[rt<<1]%=mod;
    a[rt<<1|1]%=mod;
    laz[rt]=0;
}

inline void build(int rt,int l,int r){
    if(l==r){
        a[rt]=wt[l];
        if(a[rt]>mod)a[rt]%=mod;
        return;
    }
    build(lson);
    build(rson);
    a[rt]=(a[rt<<1]+a[rt<<1|1])%mod;
}

inline void query(int rt,int l,int r,int L,int R){
    if(L<=l&&r<=R){res+=a[rt];res%=mod;return;}
    else{
        if(laz[rt])pushdown(rt,len);
        if(L<=mid)query(lson,L,R);
        if(R>mid)query(rson,L,R);
    }
}

inline void update(int rt,int l,int r,int L,int R,int k){
    if(L<=l&&r<=R){
        laz[rt]+=k;
        a[rt]+=k*len;
    }
    else{
        if(laz[rt])pushdown(rt,len);
        if(L<=mid)update(lson,L,R,k);
        if(R>mid)update(rson,L,R,k);
        a[rt]=(a[rt<<1]+a[rt<<1|1])%mod;
    }
}
inline int qRange(int x,int y){
    int ans=0;
    while(top[x]!=top[y]){
        if(dep[top[x]]<dep[top[y]])swap(x,y);
        res=0;
        query(1,1,n,id[top[x]],id[x]);
        ans+=res;
        ans%=mod;
        x=fa[top[x]];
    }
    if(dep[x]>dep[y])swap(x,y);
    res=0;
    query(1,1,n,id[x],id[y]);
    ans+=res;
    return ans%mod;
}

inline void updRange(int x,int y,int k){
    k%=mod;
    while(top[x]!=top[y]){
        if(dep[top[x]]<dep[top[y]])swap(x,y);
        update(1,1,n,id[top[x]],id[x],k);
        x=fa[top[x]];
    }
    if(dep[x]>dep[y])swap(x,y);
    update(1,1,n,id[x],id[y],k);
}

inline int qSon(int x){
    res=0;
    query(1,1,n,id[x],id[x]+siz[x]-1);
    return res;
}

inline void updSon(int x,int k){
    update(1,1,n,id[x],id[x]+siz[x]-1,k);
}

inline void dfs1(int x,int f,int deep){
    dep[x]=deep;
    fa[x]=f;
    siz[x]=1;
    int maxson=-1;
    for(Rint i=beg[x];i;i=nex[i]){
        int y=to[i];
        if(y==f)continue;
        dfs1(y,x,deep+1);
        siz[x]+=siz[y];
        if(siz[y]>maxson)son[x]=y,maxson=siz[y];
    }
}

inline void dfs2(int x,int topf){
    id[x]=++cnt;
    wt[cnt]=w[x];
    top[x]=topf;
    if(!son[x])return;
    dfs2(son[x],topf); 
    for(Rint i=beg[x];i;i=nex[i]){
        int y=to[i];
        if(y==fa[x]||y==son[x])continue;
        dfs2(y,y);
    }
}

int main(){
    read(n);read(m);read(r);read(mod);
    for(Rint i=1;i<=n;i++)read(w[i]);
    for(Rint i=1;i<n;i++){
        int a,b;
        read(a);read(b);
        add(a,b);add(b,a);
    }
    dfs1(r,0,1);
    dfs2(r,r);
    build(1,1,n);
    while(m--){
        int k,x,y,z;
        read(k);
        if(k==1){
            read(x);read(y);read(z);
            updRange(x,y,z);
        }
        else if(k==2){
            read(x);read(y);
            printf("%d\n",qRange(x,y));
        }
        else if(k==3){
            read(x);read(y);
            updSon(x,y);
        }
        else{
            read(x);
            printf("%d\n",qSon(x));
        }
    }
}

P3038 [USACO11DEC] Grass Planting G

提交答案加入题单复制题目

题目描述

给出一棵有 n 个节点的树,有 m 个如下所示的操作:

  • 将两个节点之间的路径上的边的权值均加一。

  • 查询两个节点之间的那一条边的权值,保证两个节点直接相连。

初始边权均为 0。

输入格式

第一行两个整数 n,m,含义如上。

接下来 n−1 行,每行两个整数 u,v,表示 u,v 之间有一条边。

接下来 m 行,每行格式为 op u v,op=P 代表第一个操作,op=Q 代表第二个操作。

输出格式

若干行。对于每个查询操作,输出一行整数,代表查询的答案。

显示翻译

题意翻译

输入输出样例

输入 #1复制

复制代码
4 6 
1 4 
2 4 
3 4 
P 2 3 
P 1 3 
Q 3 4 
P 1 4 
Q 2 4 
Q 1 4 

输出 #1复制

复制代码
2 
1 
2 

说明/提示

对于 100% 的数据,2≤n≤105,1≤m≤105。

实现代码:

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
int n,m;
int fa[100001];
int d[100001];
int son[100001];
int su[100001];
int top[100001];
int id[100001];
vector<int>g[100001];
void dfs1(int x,int f,int deep)
{
	fa[x]=f;d[x]=deep;su[x]=1;int maxs=0,i;
	for(i=0;i<g[x].size();i++)
	{
		int v=g[x][i];
		if(v!=f)
		{
			dfs1(v,x,deep+1);
			su[x]+=su[v];
			if(maxs<su[v])
			{
				son[x]=v;
				maxs=su[v];
			}
		}
	}
}
void dfs2(int x,int fafa)
{
	top[x]=fafa;id[x]=++id[0];
	if(!son[x])return;
	dfs2(son[x],fafa);
	for(int i=0;i<g[x].size();i++)
	{
		int v=g[x][i];
		if(fa[x]==v||son[x]==v)continue;
		dfs2(v,v);
	}
}
struct qq
{
	int l,r,sum,add;
}q[400001];
void build(int p,int l,int r)
{
	q[p].l=l;q[p].r=r;
	if(l==r)return;
	build(p*2,l,(l+r)/2);
	build(p*2+1,(l+r)/2+1,r);
}
void pd(int p)
{
	q[p*2].add+=q[p].add;q[p*2+1].add+=q[p].add;
	q[p*2].sum+=(q[p*2].r-q[p*2].l+1)*q[p].add;
	q[p*2+1].sum+=(q[p*2+1].r-q[p*2+1].l+1)*q[p].add;
	q[p].add=0;
}
void change(int p,int x,int y)
{
	if(q[p].r<x||q[p].l>y)return;
	if(q[p].l>=x&&q[p].r<=y)
	{
		q[p].sum+=q[p].r-q[p].l+1;
		q[p].add++;
		return;
	}
	pd(p);
	change(p*2,x,y);
	change(p*2+1,x,y);
	q[p].sum=q[p*2].sum+q[p*2+1].sum;
} 
int ask(int p,int x)
{
	if(q[p].r<x||q[p].l>x)return 0;
	if(q[p].l==q[p].r)return q[p].sum;
	pd(p);
	return ask(p*2,x)+ask(p*2+1,x);
}
int main()
{
	int x,y,i;
	char c;
	cin>>n>>m;
	for(i=1;i<n;i++)
	{
		cin>>x>>y;
		g[x].push_back(y);
		g[y].push_back(x);
	}
	dfs1(1,0,1);
	dfs2(1,1);
	build(1,1,n);
	while(m--)
	{
		cin>>c>>x>>y;
		if(c=='P')
		{
			while(top[x]!=top[y])
			{
				if(d[top[x]]<d[top[y]])swap(x,y);
				change(1,id[top[x]],id[x]);
				x=fa[top[x]];
			}
			if(d[x]>d[y])swap(x,y);
			change(1,id[x]+1,id[y]);
		}
		else
		{
			if(fa[x]==y)cout<<ask(1,id[x])<<endl;
			else cout<<ask(1,id[y])<<endl;
		}
	}
	return 0;
}
相关推荐
MicroTech20251 小时前
变分量子算法再升级:MLGO微算法科技滤波变分量子本征求解器推动量子计算落地
科技·算法·量子计算
折哥的程序人生 · 物流技术专研1 小时前
Java面试85题图解版 · 全系列总目录
java·开发语言·后端·面试·职场和发展
gf13211111 小时前
飞书长连接_事件订阅(接收消息,审批任务状态变更)
开发语言·python·飞书
gihigo19981 小时前
竞争性自适应重加权算法(CARS)
算法
木易 士心1 小时前
Java 跳出多层循环
java·开发语言·后端
kyle~1 小时前
C++---段错误(SIGSEGV)
linux·运维·c++·机器人
乐观勇敢坚强的老彭1 小时前
day515C++信奥循环嵌套强化03
开发语言·c++
杜子不疼.1 小时前
【C++ AI 大模型接入 SDK】 - 环境搭建
开发语言·数据库·c++
怀旧,1 小时前
【C++项目】负载均衡式在线OJ
开发语言·c++·负载均衡