RMQ算法,组合数模板,题解2023.6.1

RMQ算法:快速求区间最大值
 预处理:
 第一部分是从 i 到 i+2^{j-1}-1 ,第二部分从i+2^{j-1}到i+2^j-1 ,
 因为二进制数前一个数是后一个的两倍,那么可以把 i 到 i+2^j-1 这个区间通过2^{j-1}分成相等的两部分, 
 dp[l][k]维护的是区间 [l, l + 2^k - 1] , dp[r - (1 << k) + 1][k]维护的是区间 [r - 2^k + 1, r] 。
查询:
那么只要我们保证 r-2^k+1  <= l+2^k-1 就能保证RMQ[l, r] = min(dp[l][k], dp[r - (1 << k) + 1][k]);
RMQ算法一般用较长时间做预处理,时间复杂度为O(nlogn),然后可以在O(1)的时间内处理每次查询。

void rmq_init()  //预处理,可根据题意调整预处理
{
    for (int i = 1; i <= N; i++)
        dp[i][0] = arr[i];//初始化,dp表示从i位开始连续2^j个数的区间最值
    for (int j = 1; (1 << j) <= N; j++)
        for (int i = 1; i + (1 << j) - 1 <= N; i++)
            dp[i][j] = min(dp[i][j - 1], dp[i + (1 << j - 1)][j - 1]);
}
int rmq(int l, int r)   
{
    int k = log2(r - l + 1);   //log2的头文件
    return min(dp[l][k], dp[r - (1 << k) + 1][k]);
}​

 D - Factorization
 题意:给定N和M,使得a1*a2*...aN=M,求出有多少种组合,
 思路:将M质因数分解,看做n相同个小球放进m个盒子里,允许空盒存在, 添元素隔板法(可允许空盒子)公式:c(n+m-1,m-1),然后将这k堆的方案数相乘即可
 因为a1*a2..am=M,a1,a2..am为M的一部分,而M的质因数相乘可囊括所有M分解成N个的元素除了1,于是空盒子放1或与之相乘能组合成M的数

#include<iostream>
#include<cmath>
using namespace std;
#define ll long long
const int MOD = 1e9 + 7;
const int maxn = 1e6 + 5;
ll f[maxn];
ll g[maxn];
ll inv[maxn];
ll q_pow(ll n, ll m){   //费马小定理逆元
    int res = 1;
    while (m)
    {
        if (m & 1)
        {
            res = (res * n) % MOD;
        }
        n = (n * n) % MOD;
        m = m >> 1;
    }
    return res;
}
void fac()
{
    f[0] = 1;
    inv[0] = 1;
    for (int i = 1; i <= 1000000; i++)
    {
        f[i] = (f[i - 1] * i) % MOD;
    }
    inv[1000000] = q_pow(f[1000000], MOD - 2);  //调用
    for (int  i = 1000000 - 1; i > 0; i--)
    {
        inv[i] = (inv[i + 1] * (i + 1)) % MOD;
    }
}
ll c(ll n, ll m)    //组合计算的方法2
{
    ll res = ((f[n] * inv[n - m]) % MOD * inv[m]) % MOD;
    return res;
}
int main()
{
    fac();
    ll n, m;
    cin >> n >> m;
    ll p = 0;
    ll lim = sqrt(m);
    for (int i = 2; i <= lim; i++)
    {
        if (m % i == 0)
        {
            g[p] = 0;
            while (m % i == 0)
            {
                g[p]++;
                m = m / i;
            }
            p++;
        }
    }
    ll ans = 1;
    for (int i = 0; i < p; i++)
    {
        ans = (c(g[i] + n - 1, n - 1) * ans) % MOD;
    }
    if (m != 1) ans = (ans * n) % MOD;
    cout << ans << endl;
}

#include<algorithm>
#include<iostream>
#include<cmath>
using namespace std;
#define ll long long
const int MOD = 1e9 + 7;
const int mod = 1e9 + 7;
const int maxn = 1e6 + 5;
ll f[maxn];
ll g[maxn];
ll inv(ll a,ll m) {   //线性地推,不进行储存 
    if (a == 1)return 1;
    return inv(m % a, m) * (m - m / a) % m;
}
ll C(int b, int a) { //组合计算的快速方法1,b>=a
    ll t1 = 1, t2 = 1;
    for (int i = b; i >= (b - a + 1); i--)t1 = t1 * i % mod;
    for (int i = a; i >= 1; i--)t2 = t2 * i % mod;
    return t1 * inv(t2, mod) % mod;
}
int main()
{
    ll n, m;
    cin >> n >> m;
    ll p = 0;
    ll lim = sqrt(m);
    for (int i = 2; i <= lim; i++)
    {
        if (m % i == 0)
        {
            g[p] = 0;
            while (m % i == 0)
            {
                g[p]++;
                m = m / i;
            }
            p++;
        }
    }
    ll ans = 1;
    for (int i = 0; i < p; i++)
    {
        ans = (C(g[i] + n - 1, n - 1) * ans) % MOD;
    }
    if (m != 1) ans = (ans * n) % MOD;
    cout << ans << endl;
}


 

遗迹探险
思路:dp,对于传送门的处理采用反向dp处理((n,m)到传送门),判断不经过传送门,和经过不同传送门的最大值
 

#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize(fast) 
#include<iostream>
#include<algorithm>
#include<map>
#include<set>
#include<queue>
#include<cstring>
#include<math.h>
#include<map>
#include<vector>
#include<stack>
#define ms(x,y) memset(x,y,sizeof x)
#define int long long 
typedef long long ll;
const int maxn = 1e3 + 10, INF = 1e18;
using namespace std;
int a[maxn][maxn];
int dp[maxn][maxn];
int dp1[maxn][maxn];
struct node {
    int x, y;
};
void solve() {
    int  n, m;
    cin >> n >> m;
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            cin >> a[i][j];
        }
    }
    for (int i = 1; i <= n; i++) {     
        for (int j = 1; j <= m; j++) {
            if (i == 1)dp[i][j] = dp[i][j - 1] + a[i][j];
            else if (j == 1)dp[i][j] = dp[i - 1][j] + a[i][j];
            else {
                dp[i][j] = max(dp[i][j - 1] + a[i][j], dp[i - 1][j] + a[i][j]);
            }
        }
    }
    for (int i = n; i >= 1; i--) {        //反向dp,从(n,m)到传送门的处理
        for (int j = m; j >=1; j--) {
            if (i == n)dp1[i][j] = dp1[i][j +1] + a[i][j];
            else if (j == m)dp1[i][j] = dp1[i +1][j] + a[i][j];
            else {
                dp1[i][j] = max(dp1[i][j + 1] + a[i][j], dp1[i + 1][j] + a[i][j]);
            }
        }
    }
    int t;
    cin >> t;
    while (t--) {         
        int k;
        cin >> k;
        node N[10];
        for (int i = 1; i <= k; i++) {
            cin >> N[i].x >> N[i].y;
        }
        int ans=0;
        ans = max(ans,dp[n][m]);
        for (int i = 1; i <= k; i++) {
            for (int j = 1; j <= k; j++) {
                if (i == j)
                    continue;
                ans = max(ans, dp[N[i].x][N[i].y] + dp1[N[j].x][N[j].y]);
            }
        }
        cout << ans << '\n';
    }
}
signed main()
{
    ios::sync_with_stdio(false);
    solve();
}


D - Summer Vacation
题意:给定n个任务,任务为a天,奖励b,有m天,一天可领一个任务,不能重复,任务可同时进行
 思路:先结构体按天数排序,之后优先队列先处理时间少奖金高的
 

#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize(fast) 
#include<iostream>
#include<algorithm>
#include<map>
#include<set>
#include<queue>
#include<cstring>
#include<math.h>
#include<map>
 #include<vector>
 #include<stack>
#define ms(x,y) memset(x,y,sizeof x)
typedef long long ll;
const int maxn=2e5+10,INF = 1e18 ; 
using namespace std;
int a[maxn];
int b[maxn];
struct node {
    int day, v;
}z[maxn];
bool cmp(node A,node B) {   //按天数从小到大排序
    return A.day < B.day;
}
 void solve(){           
     int n, m;
     cin >> n >> m;
     for (int i = 1; i <= n; i++) {
         int day, v;
         cin >> day >> v;
         z[i].day = day;
         z[i].v = v;
     }
     sort(z + 1, z + 1 + n, cmp);
     priority_queue<int>q;
     int flag = 1;
     int ans = 0;
     for (int i = 1; i <= m; i++) {   //不为空,一天领一任务
         while (z[flag].day <= i && flag <= n) {   //i表示剩下i天,表示第m-i+1接这个任务,先处理时间少奖金高的,
             q.push(z[flag].v);
             flag++;
         }
         if (!q.empty()) {
             ans += q.top();
             q.pop();
         }
     }
     cout << ans << '\n';
 }
signed main()
{
    ios::sync_with_stdio(false);
        solve();
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值