2020牛客暑期多校训练营(第十场)

题目链接

C - Decrement on the Tree
题意: 给你一颗有边权的无根树,你可以每次选取一条路径使得该路径上所有的边权减少1,现在有q次修改询问使得所有边权变为0的最少操作次?
思路: 由于有多次询问,所以每次修改询问,我们要做到复杂度为O(1)或者O(logn),所以比赛的时候一直在想有什么数据结构可以维护路径。但是这道题你需要将其转化一下,他就变得容易了。题目可以转化成算每个点作为路径端点的贡献,这样只需要算每个端点的贡献了。
接下来如何算每个端点的贡献呢?
考虑与一个点所有相连的边总权值与最大权值,要使得这个点不作为端点,我们每次最优选取两条边为同一条路径,所有就会出现三种情况:
1)相邻边权的最大值(max)大于其余剩下的相邻边权总和(rest),贡献为max-rest
2)相邻边权的最大值小于其余剩下的相邻边权总和且该点所有相邻边总边权为奇数,贡献为1
3)相邻边权的最大值小于其余剩下的相邻边权总和且该点所有相邻边总边权且为偶数,贡献为0
这样我们就能算每个点的贡献了,但是如何能做到快速修改呢
我们可以使用stl中的multiset进行修改即可
因为我们算出所有点的总贡献,一条路径有两个端点,所有最终得到的答案需要/2
代码:

#include<bits/stdc++.h>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<stack>
#include<queue>
#define INF 0x3f3f3f3f
#define lowbit(x) x & -x
#define lson root<<1,l,mid
#define rson root<<1|1,mid+1,r
using namespace std;
typedef long long ll;
const int mod=1e9+7;
const int N=1e5+5;
struct node{
   
    int u,v,w;
}edge[N];

ll sum[N];
multiset<int> s[N];
ll ans;
ll get(int x){
   
    auto t=s[x].end();
    t--;
    if(*t>sum[x]-*t)return *t-(sum[x]-*t);
    if(sum[x]%2==1)return 1;
    return 0;
}

int main(){
   
#ifdef Mizp
    freopen("in.txt","r",stdin);
#endif
    int n,q;
    scanf("%d%d",&n,&q);
    for(int i=1;i<n;i++){
   
        scanf("%d%d%d",&edge[i].u,&edge[i].v,&edge[i].w);
        sum[edge[i].u]+=edge[i].w;
        sum[edge[i].v]+=edge[i].w;
        s[edge[i].u].insert(edge[i].w);
        s[edge[i].v].insert(edge[i].w);
    }
    //cout<<get(2)<<endl;
    for(int i=1;i<=n;i++)
        ans+=get(i);
    
    printf("%lld\n",ans/2);
    while(q--){
   
        int p,w;
        scanf("%d%d",&p,&w);
        ans-=get(edge[p].u);ans-=get(edge[p].v);
		s[edge[p].u].erase(s[edge[p].u].lower_bound(edge[p].w));
		s[edge[p].v].erase(s[edge[p].v].lower_bound(edge[p].w));
		sum[edge[p].u]-=edge[p].w;sum[edge[p].v]-=edge[p].w;
		edge[p].w=w;
		sum[edge[p].u]+=edge[p].w;sum[edge[p].v]+=edge[p].w;
		s[edge[p].u].insert(w);s[edge[p].v].insert(w);
		ans+=get(edge[p].u);ans+=get(edge[p].v);
        printf("%lld\n",ans/2);
    }
    return 0;
}

I - Tournament
题意: 现在有 n 个队伍参加比赛,任意两个队伍之间都要进行一次比赛,也就是共需要进行 n * ( n - 1 ) / 2 次比赛,对于每个队伍来说,必须要在第一场比赛的时候到达赛场,在最后一场比赛结束后离开赛场,在赛场上呆的时间即为贡献,现在求出一种比赛的安排顺序,使得每个队伍的贡献之和最小
思路: 构造题,比赛构造了很久也构造不出来,具体思路请参考他人优秀题解
代码:

#include<bits/stdc++.h>
#include<iostream>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值