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();
}