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;
}