1、模拟
2、贪心
3、前缀和
4、猜结论
5、双指针
6、期望DP
1. 蘑菇炸弹
思路:一个简单的暴力模拟。
#include <bits/stdc++.h>
using namespace std;
int main()
{
int n;
cin >> n;
vector<int>a(n , 0);
for(int i = 0 ; i < n ; i ++)
cin >> a[i];
int cnt = 0;
for(int i = 1 ; i < n - 1; i ++){
if(a[i] >= a[i - 1] + a[i + 1])
cnt++;
}
cout << cnt;
return 0;
}
2. 构造数字
思路:考虑正整数中每一位的贡献:可以发现位数越大的位置贡献越大,因此将M优先分配给高位即可。
#include <bits/stdc++.h>
using namespace std;
int main()
{
int n , m;
cin >> n >> m;
for(int i = 0 ; i < n ; i ++){
int x = min(m , 9);
cout << x;
m -= x;
}
return 0;
}
3. 小蓝的金牌梦
思路: 首先考虑若已知长度为,如何快速求出子数组最大值,可以用前缀和来O(1)的去解决,然后遍历所有的子数组左端点,时间复杂度总共。接下来考虑所有质数情况:
范围内的质数共有9592个,考虑遍历所有情况:总共的复杂度为约为,直接暴力做还是能过的,所以直接暴力冲就行。
#include <bits/stdc++.h>
using namespace std;
#define LL long long
#define pb push_back
#define x first
#define y second
#define endl '\n'
#define int long long
const LL maxn = 4e05+7;
const LL N = 5e05+10;
const LL mod = 1e09+7;
const int inf = 0x3f3f3f3f;
const LL llinf = 5e18;
typedef pair<int,int>pl;
priority_queue<LL , vector<LL>, greater<LL> >mi;//小根堆
priority_queue<LL> ma;//大根堆
LL gcd(LL a, LL b){
return b > 0 ? gcd(b , a % b) : a;
}
LL lcm(LL a , LL b){
return a / gcd(a , b) * b;
}
int n , m;
vector<int>a(N , 0);
void init(int n){
for(int i = 0 ; i <= n ; i ++){
a[i] = 0;
}
}
vector<LL>prime;//存储素数
bool vis[N+5];
void su()
{
for(int i=2;i<=N;i++)
{
if(!vis[i])
prime.pb(i);
for(int j=0;j < prime.size() && prime[j] * i <= N;j ++)
{
vis[prime[j]*i]=1;
if(i % prime[j]==0)
break;
}
}
}
void solve()
{
cin >> n;
int sum[n + 5];
sum[0] = 0;
for(int i = 1 ; i <= n ; i ++)
cin >> a[i] , sum[i] = sum[i - 1] + a[i];
int maxx = -1e9;
for(int i = 0 ; i < prime.size() ; i ++){
int len = prime[i];
if(len > n){
break;
}
for(int j = len ; j <= n ; j ++){
maxx = max(maxx , sum[j] - sum[j - len]);
}
}
cout << maxx;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cout.precision(10);
int t=1;
su();
//cin>>t;
while(t--)
{
solve();
}
return 0;
}
4. 合并石子加强版
思路:若只关注一个石头:发现最终他的贡献为其自身权值与其他所有石头权值乘积之和。而其他所有石头也是一样的。因此最终的代价和合并石子的顺序无关,只需要按顺序模拟即可。(longlong会爆,需要unsigned long long )
#include <iostream>
using namespace std;
#define int unsigned long long
signed main()
{
int n;
cin >> n;
int a[n];
for(int i = 0 ; i < n ; i ++)
cin >> a[i];
int sum = a[0];
int tot = 0;
for(int i = 1 ; i < n ; i ++){
tot += a[i] * sum;
sum += a[i];
}
cout << tot;
return 0;
}
5. 简单的LIS问题
思路:可以发现:修改一个数之后的效果最多能够使得LIS增加1。接下来考虑怎么添加能保证上升子序列增加1:
1、若当前上升子序列不包含最后一个元素,那么将最后一个元素设为就一定能使得上升子序列加一。
2、若上升子序列的开头不为0且不为第一个数,那么将第一个数改为就一定能使得上升子序列增加1。
3、修改子序列中相邻两个数之间的某个数,使得。
所以我们需要知道的是:以某个数开头往后所能形成的最长上升子序列以及以某个数结尾能够形成的最长上升子序列。分别设为以及,对应了以结尾能够形成最长上升子序列长度以及以开头能够形成的最长上升子序列长度。
对于情况1和情况2,可以直接通过函数来求解,对于情况3,可以想到将与合并,即最长上升子序列长度为,当时,则能够在之间修改一个数使得LIS增加1。
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,ans;
int a[5001],l[5001],r[5001];
signed main()
{
cin >> n;
for(int i = 1 ; i <= n ; i ++)
cin >> a[i];
for(int i = 1 ; i <= n ; i ++){
l[i] = r[i] = 1;
}
for(int i = 2 ; i <= n ; i ++){
for(int j = 1 ; j < i ; j ++){
if(a[i] > a[j])
l[i] = max(l[i] , l[j] + 1);
}
}
for(int i = n ; i >= 1 ; i --){
for(int j = n ; j > i ; j --){
if(a[i] < a[j])
r[i] = max(r[i] , r[j] + 1);
}
}
for (int i = 1;i <= n ; i ++)
{
if(i < n){//等效于将末尾设为10^1000
ans = max(ans , l[i] + 1);
}
else{
ans = max(ans , l[i]);
}
if(i != 1 && a[i] != 0){//等效于将 a1 设为0
ans = max(ans , r[i] + 1);
}
else{
ans = max(ans , r[i]);
}
for(int j = i + 2 ; j <= n ; j ++)//等效于将i ~ j 当中的一个数设为ai + 1
if (a[j] - a[i] > 1)
ans = max(ans , l[i] + r[j] + 1);
}
cout << ans;
}
6. 期望次数
思路:一道期望DP的板子题,定义为当前 时,需要达到目标M的操作期望。
定义为权值总和,定义为将 的权值。
接下来考虑转移方程:
化简之后得到:
之后就从后往前DP即可,最终输出.
#include <bits/stdc++.h>
using namespace std;
#define LL long long
#define pb push_back
#define x first
#define y second
#define endl '\n'
#define int long long
const LL maxn = 4e05+7;
const LL N = 5e05+10;
const LL mod = 998244353;
const int inf = 0x3f3f3f3f;
const LL llinf = 5e18;
typedef pair<int,int>pl;
priority_queue<LL , vector<LL>, greater<LL> >mi;//小根堆
priority_queue<LL> ma;//大根堆
LL gcd(LL a, LL b){
return b > 0 ? gcd(b , a % b) : a;
}
LL lcm(LL a , LL b){
return a / gcd(a , b) * b;
}
int n , m;
vector<int>a(N , 0);
void init(int n){
for(int i = 0 ; i <= n ; i ++){
a[i] = 0;
}
}
LL qpow(LL a , LL b)//快速幂
{
LL sum=1;
while(b){
if(b&1){
sum=sum*a%mod;
}
a=a*a%mod;
b>>=1;
}
return sum;
}
int dp[N];//从i到最终状态的期望次数
//sum - a[1] / sum dp[i] = Σ(dp[i * j] * a[i * j] / sum) + 1
// dp[i] = Σdp[i * j] * a[i * j] + sum / sum - a[i]
void solve()
{
cin >> n >> m;
int sum = 0;
for(int i = 1 ; i <= n ; i ++){
cin >> a[i];
sum += a[i];
}
for(int i = m - 1 ; i >= 1 ; i --){
for(int j = 2 ; j <= n && j * i < m ; j ++){
dp[i] += a[j] * dp[i * j];
dp[i] %= mod;
}
dp[i] += sum;
dp[i] %= mod;
dp[i] *= qpow(sum - a[1] , mod - 2);
dp[i] %= mod;
}
cout << dp[1];
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cout.precision(10);
int t=1;
// cin>>t;
while(t--)
{
solve();
}
return 0;
}