大二下第八周学习笔记

周六

D. Valid Sets(树形dp)

这题2000的数据范围提示n^2的算法

树形dp一般是O(n) 所以可以考虑以每一个根分别树形dp一次

那么设当前的根为rt,rt为集合的最大值

dp[i]表示以i为根的子树的子集数,i一定选

那么每一个儿子选的话方案是dp[v],不选的话方案是1,那么遍历儿子全部乘起来就好了

还有一个问题,就是一个集合如果最大值有多个,那么在以这些最大值为根统计的时候会重复计算

那么我们就设定标号最大或者最小的那个值才可以统计答案,所以就判断一下权值相等的时候比较标号就可以了

#include <bits/stdc++.h>
#define rep(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;

typedef long long ll;
const int N = 2e3 + 10;
const int mod = 1e9 + 7;
int a[N], n, d;
vector<int> g[N];
ll dp[N];

void dfs(int u, int fa, int rt)
{   
    dp[u] = 1;
    for(int v: g[u])
    {
        if(v == fa || a[v] > a[rt] || a[rt] - a[v] > d) continue;
        if(a[v] == a[rt] && rt > v) continue;
        dfs(v, u, rt);
        dp[u] = (dp[u] * (dp[v] + 1)) % mod;
    }
}

int main()
{   
    scanf("%d%d", &d, &n);
    _for(i, 1, n) scanf("%d", &a[i]);
    _for(i, 1, n - 1)
    {
        int u, v;
        scanf("%d%d", &u, &v);
        g[u].push_back(v);
        g[v].push_back(u);
    }

    ll res = 0;
    _for(i, 1, n)
    {
        dfs(i, 0, i);
        res = (res + dp[i]) % mod;
    }
    printf("%lld\n", res);

	return 0;
}

D. Cut(质因数分解+倍增)

这题很精彩,我想到了可以求以每个位置为起点最多可以多长,但是没想到用倍增加速

首先要求以每个位置为起点最远可以到哪,区间要满足两两互质

这个处理的话就是逆序遍历数组,存一下每个质数最早出现的位置,然后当前位置的值就是当前这个数可以拓展多远和下一个数得出的值取min

也就是得出从一个位置跳一次可以跳多远

那么倍增优化即可

#include <bits/stdc++.h>
#define rep(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;

const int N = 1e5 + 10;
vector<int> ve[N];
int f[N][20], pos[N], n, q;

int main()
{   
    scanf("%d%d", &n, &q);
    _for(i, 1, n) 
    {
        int x; scanf("%d", &x);
        for(int j = 2; j * j <= x; j++)
            if(x % j == 0)
            {
                ve[i].push_back(j);
                while(x % j == 0) x /= j;
            }
        if(x > 1) ve[i].push_back(x);
    }

    f[n + 1][0] = n + 1;
    for(int i = n; i >= 1; i--)
    {   
        int cur = 1e9;
        for(int x: ve[i])
        {
            if(pos[x]) cur = min(cur, pos[x]);
            pos[x] = i;
        }
        f[i][0] = min(cur, f[i + 1][0]);
    }

    _for(j, 1, 19)
        _for(i, 1, n + 1)
            f[i][j] = f[f[i][j - 1]][j - 1];
            
    while(q--)
    {
        int x, r, ans = 0;
        scanf("%d%d", &x, &r);
        for(int j = 19; j >= 0; j--)
            if(f[x][j] <= r)
            {
                ans += 1 << j;
                x = f[x][j];
            }
        printf("%d\n", ans + 1);
    }

	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值