CCPC Wannafly summer camp Day4

博客介绍了CCPC夏令营第四天的三道算法题目,包括C题的DAG上的dp求解,D题通过贪心策略优化序列的乘积,以及H题中涉及树形dp的染色问题,详细阐述了各题的解题思路和关键算法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Day4 还是比较简单的

C.Journey

有一个DAG,边上有权值,共有n个点和m条边你一共有T的体力,现在从1出发到点n,经过一条边,花费体力值为这条边的边权,现在你要在体力允许的情况下到达n点并尽可能经过最多的点,求出经过点的数量和路线

DAG上dp,开始读错题了。。。以为终点随意。后来以为dfs遍历一下就ok,后来发现并不是,会爆掉(这是真的蠢) d p / i , j dp/_{i,j} dp/i,j表示在点 i i i处距离 n n n的最大深度为 j j j时需要的最小花费,则有: d p n o w , i = m i n ( d p n o w , i , d p t o , i − 1 + d i s ( n o w , t o ) ) dp_{now,i}=min(dp_{now,i},dp_{to,i-1}+dis(now,to)) dpnow,i=min(dpnow,i,dpto,i1+dis(now,to))
d p n o w , i dp_{now,i} dpnow,i发生更新的时候,更新一下路径 p a t h n o w , i = t o path_{now,i}=to pathnow,i=to

#include<bits/stdc++.h>
#define ll long long
#define pii pair<int,int>
using namespace std;
const int maxn=5e3+5;
const int inf=1e9+9;
vector<pii>G[maxn];
int dp[maxn][maxn];
int path[maxn][maxn];
int vis[maxn];
int n,m,T;
void dfs(int now){
    for(pii t:G[now]){
        int to=t.first,v=t.second;
        if(!vis[to]){
            vis[to]=1;
            dfs(to);
        }
        for(int i=1;i<=n;++i){
            if(dp[now][i]>dp[to][i-1]+v){
                dp[now][i]=dp[to][i-1]+v;
                path[now][i]=to;
            }
        }
    }
}
int main()
{
    ios::sync_with_stdio(0);
    for(int i=0;i<maxn;++i) for(int j=0;j<maxn;++j) dp[i][j]=inf;
    cin>>n>>m>>T;
    for(int i=1;i<=m;++i){
        int a,b,c;cin>>a>>b>>c;
        G[a].push_back({b,c});
    }
    dp[n][1]=0;
    dfs(1);
    int dep;
    for(int i=n;i>=1;--i){
        if(dp[1][i]<=T){
            dep=i;
            cout<<i<<endl;
            break;
        }
    }
    int now=1;
    for(int i=0;i<dep;++i){
        cout<<now<<" ";
        now=path[now][dep-i];
    }
}

D.Maxim and Array

对于一个有n个元素的序列 A A A,你可以进行k次操作,每次可以把任意一个元素加上x。如何修改可以使 ∏ 1 i ≤ n \prod\limits_{1}^{i\leq n} 1in最小

贪心,我们可以知道,修改最小的值,可以使得这次修改对当前答案的贡献最大。然后讨论一下正负,如果 ∏ 1 i ≤ n 0 \prod\limits_{1}^{i\leq n}0 1in0,先看能不能把最小的变成负数,(其实只要一直绝对值处理最小的就ok,有一个优先队列维护)

#include <bits/stdc++.h>
#define ll long long
#define pli pair<ll ,int>
using namespace std;
const int maxn=2e5+5;
ll num[maxn];
ll abss(ll x)
{
    return x < 0 ? -x : x;
}
struct cmp
{
    bool operator()(pli a, pli b)
    {

        return abss(a.first) > abss(b.first);
    }
};
priority_queue<pli,vector<pli>,cmp>pq;
int flag=0;
int main()
{
    ios::sync_with_stdio(0);
    int n, m, x;
    cin >> n >> m >> x;
    for(int i=1;i<=n;++i)
    {
        int t;cin>>t;
        if(t<0) flag^=1;
        pq.push({t,i});
    }
    if(flag)
    {
        for(int i=1;i<=m;++i)
        {
            ll t = pq.top().first;
            int ti=pq.top().second;
            pq.pop();
            if (t < 0)  t-=x;
            else t+=x;
            pq.push({t,ti});
        }
    }
    else{
        pli t=pq.top();
        pq.pop();
        ll tv=t.first;
        int ti=t.second;
        if(abss(tv)-1ll*m*x<0)
        {
            int need=abss(tv)/x+1;
            m-=need;
            if(tv<0) tv+=1ll*need*x;
            else tv-=1ll*need*x;
            pq.push({tv,ti});
            for(int i=1;i<=m;++i)
            {
                pli v=pq.top();
                ll vv=v.first;
                int vi=v.second;
                pq.pop();
                if(vv<0) vv-=x;
                else vv+=x;
                pq.push({vv,vi});
            }
        }
        else if(abss(tv)==1ll*m*x)
        {
            pq.push({0,ti});
        } else
        {
            pq.push({tv-1ll*m*x,ti});
        }
    }
    while(!pq.empty())
    {
        pli t=pq.top();
        num[t.second]=t.first;
        pq.pop();
    }
    for(int i=1;i<=n;++i) cout<<num[i]<<" ";
}

H.树上染色

有一个n个点的树,树边有正权值,你要把其中的k个点染色,求染色的点两两之间距离之和+未染色点两两之间距离之和

开始以为是二分图,后来发现,k是给定的,不知道怎么做了。。。然后网友解释是树形dp。参考题解
d p i , j dp_{i,j} dpi,j表示以i为顶点的子树染了j个点后,这棵子树对于答案的贡献是多少。在更新的时候, t m p i tmp_i tmpi表示:在当前这棵子树下,染色i个点对(当前子树给)答案的贡献,即 t m p i = d p x , i ( x 为 顶 点 ) tmp_i=dp_{x,i}(x为顶点) tmpi=dpx,i(x)
对于每条边 ( u , v ) (u,v) (u,v)权值为c,则当前边对答案的贡献: c ∗ ( ( u 那 一 端 连 接 的 染 色 点 ∗ v 那 一 段 连 接 的 染 色 点 ) + ( u 那 一 段 的 未 染 色 点 ∗ 那 一 端 的 未 染 色 点 ) ) c*((u那一端连接的染色点*v那一段连接的染色点)+(u那一段的未染色点*那一端的未染色点)) c((uv)+(u))
进行过更新即可

#include<bits/stdc++.h>
#define ll long long
#define pii pair<int,int>
const int maxn=2e3+3;
using namespace std;

int size[maxn];
vector<pii>G[maxn];
ll dis[maxn],dp[maxn][maxn];
int n,k;
void dfs(int fa,int now){
    size[now]=1;
    for(int i=0;i<G[now].size();++i){
        int to=G[now][i].first,v=G[now][i].second;
        if(to==fa) continue;
        dfs(now,to);
        memset(dis,0,sizeof(dis));
        int num1=min(k,size[now]),num2=min(k,size[to]);
        for(int i=0;i<=num1;++i){
            for(int j=0;j<=num2;++j){
                if(i+j>k) break;
                ll t=1ll*v*(j*(k-j)+(size[to]-j)*(n-size[to]-k+j));
                dis[i+j]=max(dis[i+j],dp[now][i]+dp[to][j]+t);
            }
        }
        for(int i=0;i<=k;++i) dp[now][i]=dis[i];
        size[now]+=size[to];//先处理完了再染色,否则会重复计数
    }
}
int main()
{
    ios::sync_with_stdio(0);
    cin>>n>>k;
    for(int i=1;i<n;++i){
        int a,b,c;
        cin>>a>>b>>c;
        G[a].push_back({b,c});
        G[b].push_back({a,c});
    }
    dfs(-1,1);
    cout<<dp[1][k]<<endl;
}

其余题目不写了~~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值