CCPC Wannafly summer camp Day3

本文深入解析了计数DP算法在MountainScenes问题中的应用,通过动态规划解决方块填充问题,并详细阐述了树上LCA算法在Tourists问题中的实现,求解树中点对之间的最短路径,适用于算法竞赛与数据结构学习。

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

Day3的题目也太难了哇

F.Mountain Scenes

做法:计数dp(我觉得是计数的。。。)
dpi,j:填充第i列,用的方块数量为j时的方案数dp_{i,j}:填充第i列,用的方块数量为j时的方案数dpi,jij
当前位于第iii列,用到的方块数量是jjj,当前列高度为kkk,

转移方程:dpi,j+k+=dpi−1,jdp_{i,j+k}+=dp_{i-1,j}dpi,j+k+=dpi1,j

emmm不过上述转移式不太明白,明白的是下一个:

dp[i][j]=∑k=0j≥kdp[i−1][j−k]dp[i][j]=\sum \limits_{k=0}^{j\ge k}dp[i-1][j-k]dp[i][j]=k=0jkdp[i1][jk]

欢迎dalao留言解释QAQ

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e2+2,mod=1e9+7;
int dp[maxn][maxn*maxn];
int n,w,h;
int main()
{
    cin>>n>>w>>h;
    dp[0][0]=1;
    for(int i=1;i<=w;++i)
    {
        for(int j=0;j<=n;++j)
        {
                if(dp[i-1][j])
            for(int k=0;k<=h;++k)
            {
                if(j+k>n) break;
                {
                    dp[i][j+k]+=dp[i-1][j];
                    dp[i][j+k]%=mod;
                    //printf("dp[%d][%d]=%d\n",i,j+k,dp[i][j+k]);
                }
            }
        }
    }
    long long ans=0;
    for(int i=0;i<=n;++i) ans+=dp[w][i],ans%=mod;
    int tmp=min(h+1,n/w+1);
    ans-=tmp;
    if(ans<0) ans+=mod;
    cout<<ans<<endl;
}

I.Tourists

有一棵树,对于树的每个点,要走完所有这个点整倍数的点,花费是两点之间的经过的点的数量(包括起止点),即两点之间边数量+1,求整张图走完所有点的花费

因为这是一棵树,所以点与点之间仅有一条路径,lca板子题

#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+5;
vector<int>G[maxn];
int n;
int depth[maxn],father[maxn][40];
void dfs(int fa,int now)
{
    for(int to:G[now])
    {
        if(to==fa) continue;
        depth[to]=depth[now]+1;
        father[to][0]=now;
        dfs(now,to);
    }
}
void bz()
{
    for(int i=1;i<=30;++i)
    {
        for(int j=1;j<=n;++j)
        {
            father[j][i]=father[father[j][i-1]][i-1];
        }
    }
}
int LCA(int u,int v)
{
    if(depth[u]>depth[v]) swap(u,v);
    int flag=(u==1 && v==2);
    if(flag)
    {
        int q=1;
    }
    int delt=depth[v]-depth[u];
    int len=0;
    for(int i=0;i<=30;++i) {
        if ((1 << i) & delt) {
            v = father[v][i], len += (1 << i);
            int q=1;
        }
    }
    if(u==v)
        return len+1;
    for(int i=30;i>=0;--i)
    {
        if(father[u][i]!=father[v][i])
        {
            len+=(1<<i)*2;
            u=father[u][i];
            v=father[v][i];
        }
    }
    u=father[u][0];
    return len+3;
}
int main()
{
    //freopen("in.in","r",stdin);
    scanf("%d",&n);
    for(int i=1;i<n;++i)
    {
        int a,b;scanf("%d%d",&a,&b);
        G[a].push_back(b);
        G[b].push_back(a);
    }
    dfs(-1,1);
    bz();
    long long ans=0;
    for(int i=1;i<=n/2;++i)
    {
        for(int j=2*i;j<=n;j+=i)
        {
            ans+=LCA(i,j);
            //printf("%d to %d is %d\n",i,j,LCA(i,j));
        }
    }
    cout<<ans<<endl;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值