难度:★☆☆☆☆
斐波那契数列。可以用递推公式直接做,也可以递归(记忆化dfs)
★★☆☆☆
递推
# include <iostream>
using namespace std;
long long f[1010]={0};
int main(void)
{
int n; cin >> n;
f[0] = f[1] = 1;
for (int i=2; i<=n; i++)
for (int j=0; j*2<=i; j++) f[i]+=f[j];
cout << f[n];
return 0;
}
★★★☆☆
想法一:
免费馅饼(数塔)是一维的,此题是二维的
dp[k][i][j] 表示第k秒机器人出现在(i, j)的最多收益
状态转移方程:dp[k][i][j] = 第k秒(i,j)出现鼹鼠?1:0 + max(dp[k-1][i][j],dp[k-1][i-1][j],dp[k-1][i][j-1],dp[k-1][i+1][j],dp[k-1][i][j+1])
但,即使用滚动数组,空间的问题解决了,但是还是会超时
想法二:
dp[i] 表示截止到第i个鼹鼠出现的时刻,且打死第i个鼹鼠,最多收益
状态转移方程:dp[i] = 1+max(distance(i,k)<=time[i]-time[k]?dp[k]:0)
复杂度O(m*m) ac
注意:法二种n没有用,不要把m写成n
优化:
记录打到第i只为止答案最大值,可以剪枝
# include <cstdio>
# include <algorithm>
# include <iostream>
using namespace std;
const int M = 10010;
int dp[M], x[M], y[M], tim[M];
int mx[M] = {0};
int main(void)
{
int n, m; scanf("%d%d", &n, &m);
for (int i=1; i<=m; i++) scanf("%d%d%d", &tim[i], &x[i], &y[i]);
for (int i=1; i<=m; i++)
{
dp[i] = 1; //打死自己这儿的一只
for (int k=i-1; k>=1; k--)
{
if (mx[k] + 1 <= dp[i]) break; // 剪枝
if (abs(x[i]-x[k]) + abs(y[i]-y[k]) <= tim[i]-tim[k]) dp[i] = max(dp[i], dp[k]+1);
}
mx[i] = max(mx[i-1], dp[i]);
}
cout << mx[m];
return 0;
}
题意是把n分成几个数的和,问这几个数的lcm一共多少种
lcm来源于n分出来的质因数
# include <cstdio>
# include <algorithm>
# include <iostream>
# include <cstring>
using namespace std;
typedef long long i64;
i64 dp[1010][1010] = {0}; //考虑前i个质数,凑的和为j,几种lcm
bool vis[1010];
int p[1010], cnt;
void getprime(int n) //线性筛质数,筛到n,个数是cnt
{
memset(vis, false, sizeof(vis));
cnt = 0;
for (int i=2; i<=n; i++)
{
if (!vis[i]) p[++cnt] = i;
for (int j=1; j<=cnt && i*p[j]<=n; j++)
{
vis[i*p[j]] = true;
if (i % p[j] == 0) break;
}
}
}
int main(void)
{
int n; cin >> n;
getprime(n);
dp[0][0] = 1;
for (int i=1; i<=cnt; i++) //<=n的质数一共cnt
{
for (int j=0; j<=n; j++)
{
dp[i][j] += dp[i-1][j]; //不选
for (int cost = p[i]; j+cost<=n; cost *= p[i]) //第i个质数选k个
{
dp[i][j+cost] += dp[i-1][j];
}
}
}
long long ans;
for (int i=0; i<=n; i++) ans +=dp[cnt][i];
cout << ans;
return 0;
}
★★★★☆
喜欢这道题,题面好理解
调试好久,数据范围很大,modp和i64要时刻注意
// 递推可以和矩阵快速幂结合
# include <cstdio>
# include <algorithm>
# include <iostream>
# include <cstring>
using namespace std;
typedef long long i64;
i64 p; //mod p
struct Matrix {
int n, m; //n行m列
i64 M[4][4];
};
void printmm(Matrix C)
{
for (int i = 1; i <= C.n; i++)
{
for (int j = 1; j <= C.m; j++)
printf("%lld\t", C.M[i][j]);
printf("\n");
}
printf("\n");
}
Matrix mpl(Matrix A, Matrix B)
{
//printf("Matrix A\n"); printmm(A);
//printf("Matrix B\n"); printmm(B);
Matrix C;
C.n = A.n;
C.m = B.m;
memset(C.M, 0, sizeof(C.M));
for (int i = 1; i <= C.n; i++)
for (int j = 1; j <= C.m; j++)
for (int k = 1; k <= A.m; k++)
C.M[i][j] = (C.M[i][j] + A.M[i][k] * B.M[k][j]%p) % p;
//printf("Matrix C\n"); printmm(C);
return C;
}
Matrix pow(Matrix A, long long n) //矩阵快速幂 循环实现
{
//printf("矩阵的%lld次方\n", n);
//printf("Matrix\n"); printmm(A);
Matrix ans;
ans.n = ans.m = A.n; //方阵
memset(ans.M, 0, sizeof(ans.M));
for (int i = 1; i <= ans.n; i++) ans.M[i][i] = 1; //单位阵
//printf("Matrix ans\n"); printmm(ans);
while (n > 0)
{
if (n % 2) ans = mpl(ans, A);
A = mpl(A, A);
n /= 2;
}
//printf("Matrix ans\n"); printmm(ans);
return ans;
}
int main(void)
{
i64 pow1[20], pow9[20]; // 预处理,i位数的范围是pow1[i]~pow9[i]
pow1[1] = 1; pow9[1] = 9;
for (int i = 2; i <= 19; i++)
{
pow1[i] = pow1[i - 1] * 10;
pow9[i] = pow1[i] * 10 - 1;
}
pow1[1] = 0;
i64 n;
cin >> n >> p;
Matrix D; // D(1)
D.n = 3; D.m = 1;
D.M[1][1] = 1; D.M[2][1] = 1; D.M[3][1] = 1;
// 要乘n次的矩阵
// f(i) = f(i-1) * 10^x + i (x是i的位数)
// 所以分段,i是1位的——1~9,2位的——10~99,3位的——100~999,... 一直到19位——1e18
Matrix E;
E.n = E.m = 3;
E.M[1][1] = 1; E.M[1][2] = 1; E.M[1][3] = 1; //E.M[1][1]每次都乘10
E.M[2][1] = 0; E.M[2][2] = 1; E.M[2][3] = 1;
E.M[3][1] = 0; E.M[3][2] = 0; E.M[3][3] = 1;
Matrix F;
F.n = F.m = 3;
memset(F.M, 0, sizeof(F.M));
for (int i = 1; i <= 3; i++) F.M[i][i] = 1; //单位阵
//printf("Matrix F\n"); printmm(F);
int bit = 1;
i64 lastcnt = 1;
while (n >= pow1[bit])
{
//printf("bit=%d\n", bit);
E.M[1][1] *= 10;
E.M[1][1] %= p;
//printf("Matrix E\n"); printmm(E);
if (n >= pow9[bit]) // 如果可以到999...99
{
F = mpl(pow(E, pow9[bit] - lastcnt), F); //注意不能颠倒,因为矩阵乘法有顺序
//printf("Matrix F\n"); printmm(F);
}
else
{
F = mpl(pow(E, n - lastcnt), F);
//printf("Matrix F\n"); printmm(F);
break;
}
lastcnt = pow9[bit];
bit++;
}
Matrix ans = mpl(F, D);
cout << (ans.M[1][1]+p)%p;
return 0;
}
stl的vector+线段树(单点修改,都不用懒惰标记,代码很短)
先根据插入序列弄出最后的排列,按顺序填数,填数规则是统计在这个数之前出现的全部数的最大值+1
统计N次最大值,用线段树优化
# include <iostream>
# include <vector>
# include <algorithm>
# include <cstring>
using namespace std;
const int N = 100010;
int pos[N];// 数字i最后在哪
int whoin[N]; //谁在第i个
vector<int> v;
int sgt[N<<2]; // 只有一个关键词 max
void write(int pos, int num, int rt, int le, int ri)
{
if (le == ri) //到了pos
{
sgt[rt] = num;
return;
}
if (pos <=(le+ri)/2) write(pos, num, rt*2, le, (le+ri)/2);
else write(pos, num, rt*2+1, (le+ri)/2+1, ri);
sgt[rt] = max(sgt[rt*2], sgt[rt*2+1]);
}
int findmax(int L, int R, int rt, int le, int ri) //要查找区间是L~R
{
if (L > ri || R < le) return 0;
else if (L <= le && R >= ri) return sgt[rt];
return max(findmax(L, R, rt*2, le, (le+ri)/2), findmax(L, R, rt*2+1, (le+ri)/2+1, ri));
}
int main(void)
{
int n; cin >> n;
for(int i = 1; i <= n; i++) //用vector比自己写链表要快
{
int pos; cin >> pos;
v.insert(v.begin() + pos, i);
}
for (int i=0; i<n; i++)
{
whoin[i+1] = v[i];
pos[v[i]] = i+1;
}
// 建线段树,全为零
memset(sgt, 0, sizeof(sgt));
int ans = 0;
for (int i=1; i<=n; i++) //从1开始依次填
{
int m = findmax(1, pos[i]-1, 1, 1, n); //1~pos[i]-1
write(pos[i], m+1, 1, 1, n); //pos[i] 改为 m+1
ans = max(ans, m+1);
cout << ans << endl;
}
return 0;
}