P3128 [USACO15DEC] LCA + 差分

本文详细介绍了USACO竞赛中的P3128题目,该题目涉及树上的最大流问题。通过使用树上差分、倍增算法求解LCA(最近公共祖先),并动态维护路径覆盖次数。最终通过前缀和更新答案,得到覆盖次数最多的点。代码实现包括DFS遍历、LCA计算和路径贡献更新等关键步骤。
摘要由CSDN通过智能技术生成
题意

传送门 P3128 [USACO15DEC]Max Flow

题解

求树上被运输路线覆盖次数最多的点。考虑树上差分,倍增求 L C A LCA LCA,设路径的两个端点分别为 u , v u,v u,v,差分数组为 d d d,父节点数组为 p a r par par,则对于每一条路径
d u ← d u + 1 d_u\leftarrow d_u+1 dudu+1 d v ← d v + 1 d_v\leftarrow d_v+1 dvdv+1 d L C A ← d L C A − 1 d_{LCA}\leftarrow d_{LCA}-1 dLCAdLCA1 d p a r [ L C A ] ← d p a r [ L C A ] − 1 d_{par[LCA]}\leftarrow d_{par[LCA]}-1 dpar[LCA]dpar[LCA]1 最后回溯从叶子节点计算前缀和,更新答案即可。

#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <vector>
using namespace std;
#define maxlg 16
#define maxn 50005
int N, K, res, lg[maxn], dif[maxn], par[maxlg][maxn], dep[maxn];
vector<int> G[maxn];

void dfs(int p, int v, int d)
{
    par[0][v] = p, dep[v] = d;
    for (int i = 1; i <= lg[d]; ++i)
        par[i][v] = par[i - 1][par[i - 1][v]];
    for (int i = 0; i < (int)G[v].size(); ++i)
    {
        int u = G[v][i];
        if (u != p)
            dfs(v, u, d + 1);
    }
}

int lca(int u, int v)
{
    if (dep[u] > dep[v])
        swap(u, v);
    while (dep[v] > dep[u])
        v = par[lg[dep[v] - dep[u]]][v];
    if (u == v)
        return v;
    for (int i = lg[dep[v]]; i >= 0; --i)
    {
        if (par[i][v] != par[i][u])
            v = par[i][v], u = par[i][u];
    }
    return par[0][v];
}

void sum(int p, int v)
{
    for (int i = 0; i < (int)G[v].size(); ++i)
    {
        int u = G[v][i];
        if (u != p)
        {
            sum(v, u);
            dif[v] += dif[u];
        }
    }
    res = max(res, dif[v]);
}

int main()
{
    scanf("%d%d", &N, &K);
    for (int i = 1; i < N; ++i)
    {
        int u, v;
        scanf("%d%d", &u, &v);
        G[u].push_back(v);
        G[v].push_back(u);
    }
    lg[0] = -1;
    for (int i = 1; i <= N; ++i)
        lg[i] = lg[i - 1] + (1 << (lg[i - 1] + 1) == i);
    dfs(0, 1, 0);
    for (int i = 0; i < K; ++i)
    {
        int s, t;
        scanf("%d%d", &s, &t);
        int p = lca(s, t);
        ++dif[s], ++dif[t];
        --dif[p], --dif[par[0][p]];
    }
    sum(0, 1);
    printf("%d\n", res);
    return 0;
}
以下是P4087 [USACO17DEC]Milk Measurement的c++代码: ```c++ #include<bits/stdc++.h> using namespace std; int n,d,i,x,minn=1e9,maxn=-1e9,sum=7;//注意sum要初始化为7,因为一开始有三个人挤奶! map<int,int> mp; struct node{ int day,milk,id;//day表示某一天,milk表示这一天的产奶量,id表示这头牛的编号 }a[100010]; bool cmp(node x,node y){ return x.day<y.day; } int main(){ scanf("%d%d",&n,&d); for(i=1;i<=n;i++){ scanf("%d%d%d",&a[i].day,&a[i].id,&a[i].milk); minn=min(minn,a[i].id);//记录最小的牛的编号 maxn=max(maxn,a[i].id);//记录最大的牛的编号 } sort(a+1,a+n+1,cmp);//排序 for(i=1;i<=n;i++){ int p=a[i].id; mp[p]+=a[i].milk;//记录每头牛产奶总量 if(mp[p]-a[i].milk>=mp[minn]&&mp[p]>=mp[minn]){//如果这头牛的产奶总量减去这一天的产奶量后等于最小产奶量且这头牛的产奶总量大于等于最小产奶量 sum--; } if(mp[p]>=mp[maxn]&&mp[p]-a[i].milk<mp[maxn]){//如果这头牛的产奶总量大于等于最大产奶量且这头牛的产奶总量减去这一天的产奶量小于最大产奶量 sum++; } if(mp[p]-a[i].milk<mp[maxn]&&mp[p]>=mp[maxn]){//如果这头牛的产奶总量减去这一天的产奶量小于最大产奶量且这头牛的产奶总量大于等于最大产奶量 if(mp[maxn]-mp[p]+a[i].milk>0)sum++; } mp[p]-=a[i].milk;//减去这一天的产奶量 if(i==n||a[i].day!=a[i+1].day){//如果到了新的一天或者到了最后一天 if(mp[maxn]!=mp[a[i].id]&&mp[a[i].id]>=mp[maxn])sum++;//如果这头牛的产奶总量不等于最大产奶量且这头牛的产奶总量大于等于最大产奶量 if(mp[maxn]==mp[a[i].id]){//如果这头牛的产奶总量等于最大产奶量 if(a[i].id==maxn)sum+=0;//如果这头牛就是最大产奶量的牛,那么不需要增加计数器 else sum++;//否则需要增加计数器 } if(mp[minn]!=mp[a[i].id]&&mp[a[i].id]>=mp[minn])sum++;//如果这头牛的产奶总量不等于最小产奶量且这头牛的产奶总量大于等于最小产奶量 if(mp[minn]==mp[a[i].id]){ if(a[i].id==minn)sum+=0;//如果这头牛就是最小产奶量的牛,那么不需要增加计数器 else sum++;//否则需要增加计数器 } } } printf("%d\n",sum); return 0; } ``` 该题的解题思路是模拟,需要注意细节问题。我们可以首先将输入的数据按天数排序,然后模拟每一天挤奶的情况,并根据题目要求进行计数即可。具体细节请见代码注释。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值