CF DP题练习

< *1800

I 545C. Woodcutters *1500
可以贪心,不过我是来练 dp 的。
我们设 fi,0/1/2 代表第 i 棵树 向左倒/不砍/向右倒
可以列出方程直接写即可。

n = read();
F(i,1,n)a[i].x = read(),a[i].h = read();
f[1][0] = 1;if(a[2].x - a[1].x > a[1].h)f[1][2] = 1;
a[n+1].x = inf;
F(i,2,n){
    f[i][1] = max(f[i-1][0],max(f[i-1][1],f[i-1][2]));
    if(a[i].x - a[i-1].x > a[i].h)f[i][0] = max(f[i-1][0],f[i-1][1]) + 1;
    if(a[i].x - a[i-1].x > a[i].h + a[i-1].h)f[i][0] = max(f[i][0],f[i-1][2]+1);
    if(a[i+1].x - a[i].x > a[i].h)f[i][2] = max(f[i-1][0],max(f[i-1][1],f[i-1][2])) + 1;
}
printf("%lld\n",max(f[n][0],max(f[n][1],f[n][2])));

II 166E. Tetrahedron *1500

我们定义 fi 为走 i 步后停留到 A 点的方案数,gi 为停在 其他点 的方案数。

fi+gi=3i,易得到递推方程 fi=gi1=3i1fi1

III 1418C. Mortal Kombat Tower *1500
考虑 dp,我们令 fi,0/1 代表打到第 i 个怪兽且第 i 个怪兽是 朋友/自己 打败的。

则可以列出状态转移方程为:

fi,0=min(fi1,1+ai,fi2,1+ai+ai1) 意思是朋友打 一个或两个 的花费。

fi,1=min(fi1,0,fi2,0) 意思是自己打 不需要花费

注意一下多测不能直接 memset

IV 431C. k-Tree *1600
我们令 fi,0/1 表示路径 总和i符合/不符合 路径条数,答案即为 fn,1

fi,0=j<dfij,0 因为不能有 大于 d 的边。

fi,1=fij,0+jdfij,0

*1800 ~ *2200

I 1997C.Nikita and LCM *1900

我们令 M=lcm(a1,...an),则 a 的任何一个子序列的 lcm 一定是 M 的因数。

M 大于 a 中的任意一个数,则答案为 n
否则,我们可以以因数表示为 状态,线性转移即可。

状态可以存到 set 中利用 map 转移。
复杂度 O(nClog(C))C 大约为 500。(注意不要爆 longlong !!)

int t,n,ans;
ll a[N],mx,lc;
ll lcm(ll x,ll y){
    if(x > 1e9 || y > 1e9)return inf;
    return x * y / __gcd(x,y);
}
set<ll>st;
map<ll,int>f,v,g;
void solve(){
    st.clear(),f.clear(),v.clear();
    st.insert(1);
    f[1] = 0,ans = 0;
    for(int i = 1;i <= n;i++){
        v[a[i]] = 1;g = f;//需要一个转移数组 g
        for(auto state:st){
            ll x = lcm(state,a[i]);
            f[x] = max(f[x],g[state]+1);
            st.insert(x);
        }
    }
    for(auto state:st)
        if(!v[state])ans = max(ans,f[state]);
    printf("%lld\n",ans);
}
int main(){
    t = read();
    while(t--){
        n = read();
        lc = 1,mx = 0;
        F(i,1,n)a[i] = read(),mx = max(mx,a[i]),lc = lcm(lc,a[i]);
        if(lc > mx){
            printf("%d\n",n);
            continue;
        }
        solve();
    }
    
	return cerr << "Time : " << clock() << " ms" << endl, 0;
        
}

II CF771C Bear and Tree Jumps *2100

我们考虑对于以 x 为根的子树内的答案,设 fx,j 表示在 x 子树内,与 x 距离为 j 的节点答案和,有 j[1,k),fx,j=yson(x)fy,j1,当 j=0 时,我们分类讨论,若 y 节点离 x 节点距离不超过 k,则可以用 1 的费用到达,若距离大于 k,则我们需要找到距离为 k 的节点的答案,再同一加上 1,即 fx,0=yson(x)fy,k1+sizey

这样我们找出了以一个节点为根的答案,其他节点我们可以换根求出,复杂度 O(nk)

代码

posted @   oXUo  阅读(17)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下
网站统计
点击右上角即可分享
微信分享提示