有目录,想做什么类型的题可以跳转
目录
一:模拟 暴力 枚举 构造 思维
模拟: 根据题目意思,把对应的代码按题意展示出来
暴力:不考察算法,正常按照逻辑计算
枚举:符合题意的每一个可能的值进行运算
构造:和模拟类似
思维:按照题意相同的意思,但解法可以不依赖本身题意,通过算法,更快的解决问题
特别数的和

//模拟 暴力 枚举
//特别数的和
//https://www.lanqiao.cn/problems/191/learning/?page=1&first_category_id=1&name=%E7%89%B9%E5%88%AB%E6%95%B0
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
int const N=1e5+5;
int const M=1e6+1e5;
void init(){
}
//检查是否含有0 1 2 9这四个数
int check(int x){
int t=x;
while(t){
int p=t%10;
if(p==0||p==1||p==2||p==9)return x;
t/=10;
}
return 0;
}
void solve(){
int n;cin>>n;
int sum=0;
for(int i=1;i<=n;++i){
sum+=check(i);
}
cout<<sum;
}
//这题模拟题目意思就行
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int t=1;
//cin>>t;
//init();
while(t--)solve();
return 0;
}
反倍数

//模拟 暴力 枚举
//反倍数
//https://www.lanqiao.cn/problems/152/learning/?page=1&first_category_id=1&name=%E5%8F%8D%E5%80%8D
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
int const N=1e5+5;
int const M=1e6+1e5;
int a,b,c;
void init(){
}
//检查是否是abc的倍数
int check(int x){
if(x%a==0||x%b==0||x%c==0)return 0;
return 1;
}
void solve(){
int n;cin>>n;
cin>>a>>b>>c;
int sum=0;
for(int i=1;i<=n;++i){
sum+=check(i);
}
cout<<sum;
}
//这题模拟题目意思就行
//如果这题有多组测试用例t>1可以考虑记忆化
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int t=1;
//cin>>t;
//init();
while(t--)solve();
return 0;
}
找到最多的数

//map
//找到最多的数
//https://www.lanqiao.cn/problems/3227/learning/?page=1&first_category_id=1&problem_id=3227
#include <bits/stdc++.h>
using namespace std;
int main()
{
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
map<int,int>mp;
int n,m;
cin>>n>>m;
int t;
int maxnum,maxcount=0;
for(int i=1;i<=n;++i){
for(int j=1;j<=m;++j){
cin>>t;
++mp[t];
if(mp[t]>maxcount){
maxcount=mp[t];
maxnum=t;
}
}
}
cout<<maxnum;
return 0;
}
小蓝的漆房

//模拟 暴力 枚举
//小蓝的漆房
//https://www.lanqiao.cn/courses/21966/learning/?id=1157616&compatibility=false
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
int const N=1e4+5;
int const M=1e6+5;
int arr[N];
void solve(){
int n,k;
cin>>n>>k;
for(int i=1;i<=n;++i)cin>>arr[i];
//因为颜色不超过60种,所以没必要map之类的加快速度
int ans=0x3f3f3f3f;
//差不多是 1e9,定义一个极大值为正无穷,后面计算时进行缩小
for(int j=1;j<=60;++j){
int cnt=0;//记录涂色的次数
for(int i=1;i<=n;++i){
if(arr[i]!=j){
i+=k-1;//k-1因为内循环有++i
++cnt;
}
}
ans=min(ans,cnt);
}
cout<<ans<<'\n';
}
/*
看别人代码先看主函数
题目要求就是枚举每一种颜色,判断涂色次数最少
1贪心 显然错的 因为每次涂房子是连续的,颜色最多的房子不一定是连续最长的
2模拟 按照题意模拟每一种颜色,记录最少涂色次数
*/
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int t=1;
cin>>t;
while(t--)solve();
return 0;
}
扫雷

//模拟 思维 构造
//扫雷
//https://www.lanqiao.cn/problems/549/learning/?page=1&first_category_id=1&name=%E6%89%AB%E9%9B%B7
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
int const N=110;
int const M=1e6+1e5;
int a[N][N];
int b[N][N];
void init(){
}
int check(int x,int y){
int ans=0;
//上三
if(a[x-1][y-1])++ans;
if(a[x-1][y])++ans;
if(a[x-1][y+1])++ans;
//中二
if(a[x][y-1])++ans;
if(a[x][y+1])++ans;
//下三
if(a[x+1][y-1])++ans;
if(a[x+1][y])++ans;
if(a[x+1][y+1])++ans;
return ans;
}
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(a[i][j]==1){
b[i][j]=9;
continue;
}
b[i][j]=check(i,j);
}
}
for(int i=1;i<=n;++i){
for(int j=1;j<=m;++j){
cout<<b[i][j]<<' ';
}
cout<<'\n';
}
}
//这题不能一个数组解决
//如果只用一个数组
//会导致 后面计算的地雷数量 受到 前面计算的数量 影响
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int t=1;
//cin>>t;
//init();
while(t--)solve();
return 0;
}
小蓝和小桥的挑战

//模拟 思维 构造
//小蓝和小桥的挑战
//https://www.lanqiao.cn/problems/3238/learning/?page=1&first_category_id=1&name=%E5%B0%8F%E8%93%9D%E5%92%8C%E5%B0%8F%E6%A1%A5%E7%9A%84%E6%8C%91%E6%88%98
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
int const N=1e5+5;
int const M=1e6+5;
void solve(){
int n;
cin>>n;
int sum=0,sum0=0;
int t;
for(int i=0;i<n;++i){
cin>>t;
sum+=t;
if(t==0)++sum0,++sum;
}
if(sum==0)++sum0;
cout<<sum0<<'\n';
}
//这题很明显
//如果有0一定要+1
//和为0的情况下,+1,
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int t=1;
cin>>t;
while(t--)solve();
return 0;
}
灌溉

//模拟 构造 枚举
//灌溉
//https://www.lanqiao.cn/problems/551/learning/?page=1&first_category_id=1&name=%E7%81%8C%E6%BA%89
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
int const N=110;
int const M=1e6+1e5;
int a[N][N];
int b[N][N];
void init(){
}
void solve(){
int n,m;
cin>>n>>m;
int t;
cin>>t;
int r,c;
for(int i=0;i<t;++i){
cin>>r>>c;
a[r][c]=1;
b[r][c]=1;
}
int k;
cin>>k;
while(k--){
//要避免a[i][j]灌溉,再继续灌溉,即1个数组自己从头灌溉到尾:
for(int i=1;i<=n;++i){
for(int j=1;j<=m;++j){
if(a[i][j]){
b[i][j+1]=1;
b[i][j-1]=1;
b[i-1][j]=1;
b[i+1][j]=1;
}
}
}
//通过上一次a[i][j]的灌溉情况传导到b,然后再把b赋值回去
for(int i=1;i<=n;++i){
for(int j=1;j<=m;++j){
a[i][j]=b[i][j];
}
}
}
//k次灌溉后进行统计
int ans=0;
for(int i=1;i<=n;++i){
for(int j=1;j<=m;++j){
if(b[i][j])++ans;
}
}
cout<<ans;
}
//
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int t=1;
//cin>>t;
//init();
while(t--)solve();
return 0;
}
回文日期

//模拟 构造 枚举
//回文日期
//https://www.lanqiao.cn/problems/498/learning/?page=1&first_category_id=1&problem_id=498
#include <bits/stdc++.h>
using namespace std;
using ll=long long;
int months[] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
//函数1判断回文日期
bool func1(string s)
{
for (int i = 0; i<s.length() / 2; ++i)
{
if (s[i] != s[s.length() - 1 - i])
return false;
}
return true;
}
//函数2判断ABABBABA回文日期
bool func2(string s)
{
if (s[0] == s[2] && s[1] == s[3])
return true;
return false;
}
//函数3判断日期是否合法
bool func3(int y, int m, int d)
{
if (m == 2)
{
if (d<29)
return true;
if ((y % 4 == 0 && y % 100 != 0) || (y % 400 == 0))
{
if (d == 29)
return true;
}
return false;
}
if (months[m] >= d)
return true;
else
return false;
}
void solve(){
string s, s2; cin >> s;
int nums = stoi(s);
int year = nums / 10000;
int month = nums / 100 % 100;
int day = nums % 100;
int i, j, k;
bool ans1 = false, ans2 = false;
string s3, s4;
for (i = year; i<10000; ++i)
{
for (j = 1; j<13; ++j)
{
for (k = 1; k<32; ++k)
{
if (i == year)
{
if (j<month)
{
j = month;
k = day;
continue;
}
}
if (!func3(i, j, k))continue;
if (j<10)s3 = '0' + to_string(j);
else s3 = to_string(j);
if (k<10)s4 = '0' + to_string(k);
else s4 = to_string(k);
s2 = to_string(i) + s3 + s4;
if (func1(s2))
{
if (!ans1)
{
ans1 = true;
cout << s2 << '\n';
}
if (func2(s2))
{
cout << s2;
return;
}
}
}
}
}
}
//模拟题目意思
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int t=1;
//cin>>t;
//init();
while(t--)solve();
return 0;
}
回文日期2

//模拟 构造 枚举 思维(逆向)
//回文日期
//https://www.lanqiao.cn/problems/348/learning/?page=1&first_category_id=1&name=%E5%9B%9E%E6%96%87%E6%97%A5%E6%9C%9F
#include <bits/stdc++.h>
using namespace std;
using ll=long long;
int const N=1e5+5;
int const M=1e6+5;
int months[13]= {0,31,29,31,30,31,30,31,31,30,31,30,31};
void solve() {
int a,b;
cin>>a>>b;
int sum=0;
for(int i=1; i<13; ++i) {
for(int j=1; j<=months[i]; ++j) {
//逆向判断当前日子对应的年
int year=j%10*1000+j/10*100+i%10*10+i/10;
//计算总时间
int day=year*10000+i*100+j;
if(day>=a&&day<=b)++sum;
}
}
//将题目默认成为全部年的2月为29天,参与计算
//最后需要特判
//特判结果是9220年
//该年是闰年,所以不需要额外减少
//if(92200229>=a&&92200229<=b)--sum;
cout<<sum;
}
int main() {
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int t=1;
//cin>>t;
//init();
while(t--)solve();
return 0;
}
DNA序列修正

//模拟 思维
//DNA序列修正
//https://www.lanqiao.cn/problems/3904/learning/?page=1&first_category_id=1&name=DNA%E5%BA%8F%E5%88%97%E4%BF%AE%E6%AD%A3
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
int const N=1e5+5;
int const M=1e6+5;
map<char,char>mpchar;
map<pair<char,char>,int>mp;
void init(){
mpchar['A']='T';
mpchar['T']='A';
mpchar['C']='G';
mpchar['G']='C';
}
void solve(){
int n;
cin>>n;
string s,p;
cin>>s>>p;
for(int i=0;i<n;++i){
if(mpchar[s[i]]!=p[i]){
//只记录不匹配的DNA对
++mp[{s[i],p[i]}];
}
}
int ans=0;
//auto是c++11后开始有的,虽然可以auto [x,y]但这是17的语法,低版本只能num一个数字
//必须用&,否则num.second-=mi,不会减少
for(auto &num:mp){
char a=num.first.first;
char b=num.first.second;
//获取a,b字符,需要和mpchar[b],mpchar[a]匹配才行,画个图就知道了
int t=mp[{mpchar[b],mpchar[a]}];
int mi=min(t,num.second);
//一次替换,当前DNA对-mi,匹配的DNA对-mi,ans+=mi
ans+=mi;
num.second-=mi;
mp[{mpchar[b],mpchar[a]}]-=mi;
}
for(auto &num:mp){
ans+=num.second;
}
cout<<ans<<'\n';
}
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int t=1;
//cin>>t;
init();
while(t--)solve();
return 0;
}
无尽的石头无尽的石头:无尽的石头

//模拟 思维
//无尽的石头
//https://www.lanqiao.cn/problems/3766/learning/?page=1&first_category_id=1&name=%E6%97%A0%E5%B0%BD%E7%9A%84%E7%9F%B3%E5%A4%B4
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
int const N=1e5+5;
int const M=1e6+1e5;
int vis[M];
//get类似快速幂,获取个位上的每位数,然后除以10继续算
int get(int pos){
int ans=0;
while(pos){
ans+=pos%10;
pos/=10;
}
return ans;
}
//预处理
void init(){
int pos=1;
vis[1]=0;
int t=1;//t记录步数
while(pos<=1e6){
//now是记录下次会跳到哪里
int now=pos+get(pos);
//跳到下个位置存t++
vis[now]=t++;
//当前位置pos=now
pos=now;
}
}
void solve(){
int n;
cin>>n;
if(n==1)cout<<0<<'\n';
else if(vis[n]==0)cout<<-1<<'\n';
else cout<<vis[n]<<'\n';
}
//这题通过预处理出来1e6的数据,来解决询问,更方便
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int t=1;
cin>>t;
init();
while(t--)solve();
return 0;
}
小浩的ABC:小浩的ABC

//构造
//小浩的ABC
//https://www.lanqiao.cn/problems/4133/learning/?page=1&first_category_id=1&problem_id=4133
#include <bits/stdc++.h>
using namespace std;
using ll=long long;
int main()
{
int t;
cin>>t;
ll x,a,b,c;
ll m=1e6;
while(t--){
cin>>x;
if(x==1){
cout<<-1<<'\n';
continue;
}
if(x>m){
a=m;
}else{
a=x-1;
}
b=max(1LL,x/m);
c=x-a*b;
cout<<a<<" "<<b<<" "<<c<<"\n";
}
return 0;
}
小蓝找答案:小蓝找答案

//构造
//小蓝找答案
//https://www.lanqiao.cn/problems/5424/learning/?page=1&first_category_id=1&problem_id=5424
#include<bits/stdc++.h>
using namespace std;
int const N=2e5+10;
int a[N];
int n;
struct node{
int pos;
int num;
}sta[N];
int top;
void insert(int p,int mid){
//我们只需要考虑栈里位置最小(最靠前的)的进位
//如果位置靠前产生进位,那么后面的进位又可以从a开始编码
//insert是已经是p位置产生进位
while(top>1&&sta[top].pos>p)--top;
if(sta[top].pos==p)++sta[top].num;//说明位置一样p第二次出现,++num
else sta[++top]=(node){p,1};//否则第一次该位置出现,则产生进位1
if(top>1&&sta[top].num==mid)insert(sta[top].pos-1,mid);
//特判,如果当前num次数达到了mid的字符集,说明必须进位
//进位又把之后的while(top>1&&sta[top].pos>p)--top;
//清掉了
}
bool check(int mid){
sta[top=1]={0,0};//栈顶存0位置,0次数
for(int i=2;i<=n;++i){
if(a[i]<=a[i-1])insert(a[i],mid);//=也得进位
}
if(sta[1].num==0)return true;
//如果栈顶0位置的值为0
//说明后面的进位对0位置不产生进位
//即 用 mid期望 满足条件
return false;
}
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>n;
int lens=0;
for(int i=1;i<=n;++i){
cin>>a[i];
if(a[i]>a[i-1])++lens;
//如果每个长度都是递增的,那么全为a就行,1个字符
//判断下不需要进位的个数
}
if(lens==n){//都不需要进位
cout<<1;
return 0;
}
int l=2,r=N;//最少2个字符集,最多N个,二分
while(l<=r){
int mid=(l+r)>>1;
if(check(mid))r=mid-1;
else l=mid+1;
}
cout<<l;
return 0;
}
小蓝的无限集
//构造
//小蓝的无限集
/*
问题描述
小蓝有一个无限集,该集合生成方式如下
1.集合里面初始元素为 1。
2.如果 在集合中,则 ?·a, 十b均在集合中。
例如a=2,b=3时,该集合五个最小元素如下!
。1.
。2,因为2-1.2。
。4,因为4-1+3.
。5,因为5=2+3.
。7,因为7=5+2.
现在有t组数据,每组数组给定你3个正整数 a,b,n,
在a,b符合以上条件的情况下,你判断n是否在集合中。
输入格式
第一行输入一个正整数 t,表示测试案例组数。
接下来t行,每行输入3个正整数 a,b,n,由若干个
空格分割,含义如题所述。
输出格式
对于每组数据,如果 几 在集合中,输出 Yes,否则输
出 No。
样例输入
1
4 7
2 5 8
3 6 8
12 11 81
样例输出
NO
Yes
NO
NO
评测数据规模
1 ≤t ≤ 103,1 ≤ a, b,n < 10°.
*/
#include <bits/stdc++.h>
using namespace std;
using ll=long long;
int main()
{
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int t;
cin>>t;
ll a,b,n;
while(t--){
cin>>a>>b>>n;
if((n-1)%b==0){
cout<<"Yes\n";
continue;
}
if(a==1){
cout<<"No\n";
continue;
}
for(ll i=a;i<=n;i*=a){
if((n-i)%b==0){
cout<<"Yes\n";
continue;
}
}
cout<<"No\n";
}
return 0;
}
二:DFS(递归)
递归:函数自身调用自身
计算函数值

//模拟 递归
//计算函数值
//https://www.lanqiao.cn/problems/5194/learning/
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
int const N=1e5+5;
int const M=1e6+1e5;
void init(){
}
int dfs(int x){
if(x==0)return 1;
if(x&1){
return dfs(x-1)+1;
}else{
return dfs(x/2);
}
}
void solve(){
int x;cin>>x;
cout<<dfs(x);
}
//这题直接模拟计算过程就行,时间复杂度大概logn
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int t=1;
//cin>>t;
//init();
while(t--)solve();
return 0;
}
数的计算

//模拟 递归 思维
//数的计算
//https://www.lanqiao.cn/problems/760/learning/?page=1&first_category_id=1&name=%E6%95%B0%E7%9A%84%E8%AE%A1%E7%AE%97
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
int const N=1e5+5;
int const M=1e6+1e5;
int dp[N];
int n;
void init(){
}
//dfs1是常规写法,但会有重复计算的
int dfs1(int x){
int res=1;
for(int i=1;i<=x/2;++i){
res+=dfs1(i);
}
return res;
}
//dfs2是dfs1的记忆化
//比如说 当前数是 6 往后的的方案数是 dp[6]
//下一次 12 6又到 6 往后的方案数是固定的dp[6]
//不需要重复计算
int dfs2(int x){
int res=1;
for(int i=1;i<=x/2;++i){
if(dp[i]==0)dfs2(i);
res+=dp[i];
}
return dp[x]=res;
}
void solve1(){
cin>>n;
//cout<<dfs1(n);
cout<<dfs2(n);
}
//本题考查递归
//题目意思就是每次添加一个数
//这个数范围是 1 前一个数/2
//常规dfs就行,和斐波那契差不多
//本题数据量较小,所以两种方法效果一致
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int t=1;
//cin>>t;
//init();
while(t--)solve1();
return 0;
}
笨笨的机器人:笨笨的机器人

//dfs
//笨笨的机器人
//https://www.lanqiao.cn/problems/3262/learning/?page=1&first_category_id=1&problem_id=3262
#include <bits/stdc++.h>
using namespace std;
//这题要么dfs,要么dp
//属于偏位运算的 数位dp
int const M=7;
int const N=16;
int a[N];
int ans,cnt;
int n;
void dfs(int pos,int num){
if(pos==n){
++ans;
if(num==0){
++cnt;
}
return;
}
dfs(pos+1,(num+a[pos+1]+M)%M);
dfs(pos+1,(num-a[pos+1]+M)%M);
}
int main()
{
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>n;
for(int i=1;i<=n;++i){
cin>>a[i];
}
dfs(0,0);
double num=cnt*1.0/ans;
num=round(num*10000)/10000;
cout<<fixed<<setprecision(4)<<num;
return 0;
}
三:进制转换
进制:
2进制0-1
8进制0-7
16进制0-9 A-F
进制转换:任意进制转任意进制(任意进制转10进制,再,10进制转任意进制)
进制(任意进制转十进制)

//进制转换
//进制
//https://www.lanqiao.cn/problems/2489/learning/?page=1&first_category_id=1&name=%E8%BF%9B%E5%88%B6&status=2
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
int const N=1e5+5;
int const M=1e6+1e5;
void init(){
}
void solve(){
string s;
//cin>>s;
s="2021ABCD";
//字符串翻转后再处理
//是常规 高精度 环 字符串 处理的常规操作
reverse(s.begin(),s.end());
ll sum=0;
ll base=1;
int len=s.length();
for(int i=0;i<len;++i){
int num;
if(s[i]>='0'&&s[i]<='9')num=(int)(s[i]-'0');
else num=(int)(s[i]-'A'+10);
sum+=num*base;
base*=16;
}
cout<<sum;
}
//本题题意是 16进制数 转 10进制
//可以类比 任意进制 转 10进制
//把base 乘的数 变成 对应 的 权 就行
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int t=1;
//cin>>t;
//init();
while(t--)solve();
return 0;
}
进制转换(任意进制转任意进制)

//进制转换
//进制转换
//https://www.lanqiao.cn/problems/1230/learning/?page=1&first_category_id=1&name=%E8%BF%9B%E5%88%B6%E8%BD%AC%E6%8D%A2
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
int const N=1e5+5;
int const M=1e6+1e5;
void init(){
}
void solve(){
int n,m;
cin>>n>>m;
string s;
cin>>s;
//字符串翻转后再处理
//是常规 高精度 环 字符串 处理的常规操作
reverse(s.begin(),s.end());
int len=s.length();
ll base=1;
ll sum=0;
//这里和上个题进制转化一样
//任意进制转十进制
for(int i=0;i<len;++i){
int num;
if(s[i]>='0'&&s[i]<='9')num=(int)(s[i]-'0');
else num=(int)(s[i]-'A'+10);
sum+=num*base;
base*=n;
}
string ans="";
//通过 除法 和 求余 获取的是最低位的字符
//所以ans=c + ans;
//或者也可以ans+=c,最后进行字符串反转
while(sum){
int num=sum%m;
char c;
if(num>=0&&num<=9)c=(char)(num+'0');
else c=(char)(num-10+'A');
ans=c+ans;
sum/=m;
}
cout<<ans<<'\n';
}
//任意进制转任意进制
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int t=1;
cin>>t;
//init();
while(t--)solve();
return 0;
}
四:博弈论
博弈论:你做什么,我再做什么
情况有时很复杂,有时很简单
Alice和Bob的爱恨情仇

//博弈论
//Alice和Bob的爱恨情仇
//https://www.lanqiao.cn/problems/3865/learning/?page=1&first_category_id=1&name=Alice%E5%92%8CBob%E7%9A%84%E7%88%B1%E6%81%A8%E6%83%85%E4%BB%87
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
int const N=1e5+5;
int const M=1e6+1e5;
void init(){
}
void solve(){
int n,k;
cin>>n>>k;
ll t,sum=0;
for(int i=0;i<n;++i){
cin>>t;
sum+=t;
}
if(sum&1)cout<<"Alice";
else cout<<"Bob";
}
//很简单的一道博弈论
//由于Alice和Bob每次拿的都是奇数的任意次方
//即每次拿奇数个
//如果 总的饼干数量是奇数 那么一定是第一个人拿完最后一堆饼干
//如果 总的饼干数量是偶数 那么一定是第二个人拿完最后一堆饼干
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int t=1;
//cin>>t;
//init();
while(t--)solve();
return 0;
}
五:前缀和 枚举
前缀和:就是一个区间(范围)的和
通过 前缀和 - 前缀和 可以得到部分(前缀和)区间和
利用下标从1开始能处理很多prv[i]+=prv[i-1]+a[i]的i-1越界问题
算法很常见
区间次方和

//前缀和
//区间次方和
//https://www.lanqiao.cn/problems/3382/learning/?page=1&first_category_id=1&name=%E5%8C%BA%E9%97%B4%E6%AC%A1%E6%96%B9%E5%92%8C
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
ll const mod=1e9+7;
int const N=1e5+5;
int const M=1e6+1e5;
//a存原数组
ll a[6][N];
//b存前缀和
ll b[6][N];
int n,m;
void init(){
//先处理出1-n的2-5次方
for(int i=2;i<6;++i){
for(int j=1;j<=n;++j){
// * a[i][j],因为是次方 所以每次都要 % mod
a[i][j]=a[i-1][j]*a[1][j]%mod;
}
}
//i从1次方开始处理,因为从2开始 b 对于 a的1次方前缀和还没处理出来
for(int i=1;i<6;++i){
for(int j=1;j<=n;++j){
b[i][j]=(b[i][j-1]+a[i][j])%mod;
}
}
}
void solve(){
cin>>n>>m;
for(int i=1;i<=n;++i){
cin>>a[1][i];
}
//由于询问 m 是1e5级别的,所以建议预处理
init();
int l,r,k;
ll sum=0;
for(int i=0;i<m;++i){
cin>>l>>r>>k;
//前缀和作差,求区间和,常规操作
sum=(b[k][r]-b[k][l-1]+mod)%mod;
cout<<sum<<'\n';
}
}
//前缀和练习
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int t=1;
//cin>>t;
//init();
while(t--)solve();
return 0;
}
小郑的蓝桥平衡串

//前缀和
//小郑的蓝桥平衡串
//https://www.lanqiao.cn/problems/3419/learning/?page=1&first_category_id=1&problem_id=3419
#include <bits/stdc++.h>
using namespace std;
//输出一个整数,为输入字符串"中"最长平衡串的长度
//题目就是找子串,子串是平衡串的最长长度
/*常规做法,遇见L +1,R -1
//如果 a[i]和a[j]的值相等,说明i到j的字符串是平衡串
int const N=1050;
int a[N];
int main()
{
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
string s;
cin>>s;
int len=s.length()+1;
s="$"+s;
a[0]=1000;//可以不给初始值,都行
for(int i=1;i<len;++i){
if(s[i]=='L'){
a[i]=a[i-1]+1;
}else{
a[i]=a[i-1]-1;
}
}
int mx=0;
for(int i=0;i<len;++i){
for(int j=i+1;j<len;++j){
if(a[j]==a[i]){
mx=max(mx,j-i);
}
}
}
cout<<mx<<'\n';
return 0;
}*/
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
string s;
cin>>s;
int len=s.length();
map<int,int>mp;//存 字符串数字化的值 位置
mp[len]=1000;//给个初始值
//由于字符串从0开始,mp[-1]=1000的话,for循环可以从前往后
//mp[len]=1000的话,for循环可以从后往前
//mp的定义不变
int now=1000;
int mx=0;
for(int i=len-1;i>=0;--i){
if(s[i]=='L')++now;
else --now;
if(!mp[now]){//如果该串的数字化的值不存在没有pos,就存pos
mp[now]=i;
}else{//如果该值是第二次以上出现,那么计算之前该字符串数字化的位置到现在的位置
//如果for从前往后的话,需要i-mp[now];
mx=max(mx,mp[now]-i);
}
}
cout<<mx<<'\n';
return 0;
}
大石头的搬运工

//前缀和
//大石头的搬运工
//https://www.lanqiao.cn/problems/3829/learning/?page=1&first_category_id=1&problem_id=3829
#include <bits/stdc++.h>
using namespace std;
using ll=long long;
//这题算比较中等的题了
//前缀和的特点就是该点之前的数据是连续统计的,该点之后的数据是连续统计的
/*纯暴力就枚举每个点,然后遍历所以点到该点的花费
int main()
{
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int n;
cin>>n;
map<int,ll>mp;//pos value
ll w;
int p;
for(int i=1;i<=n;++i){
cin>>w>>p;
mp[p]+=w;
}
ll mi=0x3f3f3f3f3f3f3f3fLL;
for(auto &pos:mp){
ll sum=0;
for(auto &q:mp){
sum+=abs(pos.first-q.first)*q.second;
}
mi=min(sum,mi);
}
cout<<mi<<'\n';
return 0;
}*/
//数据1e5的范围想办法优化,前缀和
//本质上是枚举的点,将前面全部的石头移过来,将后面全部石头移过来
//前缀和可以优化前面一堆,后面一堆的情况
//计算完,最后在枚举每个pos
typedef struct node{
int pos;
ll weight;
}node;
int const N=1e5+5;
node nd[N];
ll prvweight[N];
ll nexweight[N];
ll prvcost[N];
ll nexcost[N];
bool cmp(const node &a,const node &b){
return a.pos<b.pos;
}
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int n;
cin>>n;
for(int i=1;i<=n;++i){
cin>>nd[i].weight>>nd[i].pos;
}
sort(nd+1,nd+1+n,cmp);
//从前往后的前缀和 重量,花费
for(int i=1;i<=n;++i){
prvweight[i]=prvweight[i-1]+nd[i].weight;
prvcost[i]=prvcost[i-1]+prvweight[i-1]*(nd[i].pos-nd[i-1].pos);
}
//从后往前的前缀和 重量,花费
for(int i=n;i>0;--i){
nexweight[i]=nexweight[i+1]+nd[i].weight;
nexcost[i]=nexcost[i+1]+nexweight[i+1]*(nd[i+1].pos-nd[i].pos);
}
ll mi=0x3f3f3f3f3f3f3f3fLL;
for(int i=1;i<=n;++i){
mi=min(mi,prvcost[i]+nexcost[i]);
}
cout<<mi<<'\n';
return 0;
}
最大数组和

//类型
//最大数组和
//https://www.lanqiao.cn/problems/3260/learning/?page=1&first_category_id=1&name=%E6%9C%80%E5%A4%A7%E6%95%B0%E7%BB%84%E5%92%8C
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
int const N=2e5+5;
int const M=1e6+1e5;
void init(){
}
ll arr[N];
ll prv[N];
void solve(){
int n,k;
cin>>n>>k;
for(int i=1;i<=n;++i){
cin>>arr[i];
}
//排序就是为了前缀和服务的
//通常 题目 没有明确规定 不改变顺序的 普通加顺序就能优化算法
sort(arr+1,arr+1+n);
for(int i=1;i<=n;++i){
prv[i]=prv[i-1]+arr[i];
}
//依然是mx设定最小值,然后枚举每个位置
ll mx=-0x3f3f3f3f3f3f3f3fLL;
for(int i=0;i<=2*k;i+=2){
mx=max(mx,prv[n-k+i/2]-prv[i]);
}
cout<<mx<<'\n';
}
//这题很容易想成贪心问题
//贪心 = 每次最优 * 每次 =全局最优
//而这道题 前面的选择(去除掉的价格) 会影响 后面的选择(计算价格)
//这种 前者选择 影响 后者选择 的情况 不能使用贪心
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int t=1;
cin>>t;
//init();
while(t--)solve();
return 0;
}
五Plus:前缀和 桶 贪心 DP(动态规划) (记忆化)
桶:思想就是一个数,我存数组里,可以通过下标索引来查找对应的元素
现在我把数组的空间开到数据范围
这样可以通过 数组[数据]==true来实现O(1)增删改查
这个数组就成为桶
动态规划:1最优子结构(条件)2递推关系式(运算规则)
1最优子结构(当前问题由一个子问题递推过来,而该子问题是当前问题更小规模的问题)
2递推关系式(如斐波那契数列f[i]=f[i-1]+f[i-2])
记忆化:通过记录该状态下的对应方案数(情况数)等,避免重复计算
例如斐波那契数列如果用数组存下f[i]的值,后面的每个值的计算只需要计算1次f[i-1],不会多次计算
小郑的蓝桥平衡串

//前缀和 桶 动态规划 记忆化 贪心
//小郑的蓝桥平衡串
//https://www.lanqiao.cn/problems/3419/learning/?page=1&first_category_id=1&name=%E5%B9%B3%E8%A1%A1%E4%B8%B2
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
int const N=1e3+5;;
int const M=1e6+1e5;
//明明1e3的N就够用了,为什么开2*N?
//后面用到了就知道了
//dp[i+N]:长度为 i 第一次出现的位置
//只记录第一次出现的位置,为了贪心
int dp[N*2];
void init() {
}
void solve() {
string s;
cin>>s;
int len=s.length();
s='$'+s;
//把dp置成负数
//长度为i的第一次出现的位置dp[i+N]=-0x3f3f3f3f
memset(dp,-0x3f3f3f3f,sizeof dp);
//sum记录的是伪串长(L-Q的差值)
//sum值如果相等
//说明 从前一次 sum值 的位置 到 现在sum值的位置
// 这之间的距离 L 和 Q 个数相同
int sum=0;
int ans=0;
for(int i=1; i<=len; ++i) {
if(s[i]=='L')++sum;
else --sum;
//因为可能全为Q,sum==-len,dp[sum]就越界异常了
if(dp[sum+N]<0) {
//小于零说明长度为i的串 第一次出现
dp[sum+N]=i;
} else {
//说明不是第一次出现
//dp[sum]就是长度为sum的串第一次出现时的位置
//实际上是dp[sum+N],避免前面说的负数越界异常
//用i-dp[sum]: 当前位置 i (串长为sum) - 上一次位置dp[sum](串长也是sum) = 实际串长
//因为sum值如果相等,说明从前一次sum值的位置到现在sum值的位置这之间的距离 L 和 Q 个数相同
//因为避免数组越界异常
//所以是 i-dp[sum+N]
ans=max(ans,i-dp[sum+N]);
}
}
cout<<ans;
}
int main() {
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int t=1;
//cin>>t;
//init();
while(t--)solve();
return 0;
}
六:差分
差分:前缀和的逆运算
常用于 先 多次区间修改 再 多次查询
区间更新

//差分
//区间更新
//https://www.lanqiao.cn/problems/3291/learning/?page=1&first_category_id=1&problem_id=3291
#include <bits/stdc++.h>
using namespace std;
int const N=1e5+5;
int a[N];
int main()
{
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
//差分相当于 用数组里的每个数 表示一个区间的数
//如果a[1]=1代表 原数组从 下标1-n每个数都+1
//如果a[10]=-1代表 原数组从 下标 10-n每个数都-1
//想要还原原数组 就进行前缀和
// 变成 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0.....n
//9个1 后面全是0,这样可以把区间信息简化成 a[1]=1,a[10]=-1
//且对l - r 进行+x操作就变成 a[l]+=x,a[r+1]-=x
//多次操作后 再通过前缀和还原原数组
//n次操作,1次查询,差分常见套路,差分差不多可以当做前缀和的逆运算
//如果边操作边查询,需要用树状数组lowbit
int n,m;
while(cin>>n>>m){//要注意的点是多组数据,cin>>n>>m
for(int i=1;i<=n;++i){
cin>>a[i];
}
for(int i=n;i>0;--i){
a[i]-=a[i-1];
}
int x,y,z;
while(m--){
cin>>x>>y>>z;
a[x]+=z;
a[y+1]-=z;
}
for(int i=1;i<=n;++i){
a[i]+=a[i-1];
cout<<a[i]<<" ";
}
cout<<'\n';
}
return 0;
}
小明的彩灯

//差分
//小明的彩灯
//https://www.lanqiao.cn/problems/1276/learning/?page=1&first_category_id=1&name=%E5%B0%8F%E6%98%8E%E7%9A%84%E5%BD%A9%E7%81%AF
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
int const N=1e5+5;
int const M=1e6+1e5;
void init(){
}
ll arr[N];
ll barr[N];
void solve1(){
int n,q;
cin>>n>>q;
for(int i=1;i<=n;++i)cin>>arr[i];
//1 不在原有数组上进行 差分
for(int i=1;i<=n;++i)barr[i]=arr[i]-arr[i-1];
int l,r,x;
for(int i=0;i<q;++i){
cin>>l>>r>>x;
barr[l]+=x;
barr[r+1]-=x;
}
for(int i=1;i<=n;++i){
barr[i]+=barr[i-1];
if(barr[i]<0)cout<<0<<' ';
else cout<<barr[i]<<' ';
}
cout<<'\n';
}
void solve2(){
int n,q;
cin>>n>>q;
for(int i=1;i<=n;++i)cin>>arr[i];
//2 在原有数组上进行 差分
for(int i=n;i>0;--i)arr[i]=arr[i]-arr[i-1];
int l,r,x;
for(int i=0;i<q;++i){
cin>>l>>r>>x;
arr[l]+=x;
arr[r+1]-=x;
}
for(int i=1;i<=n;++i){
arr[i]+=arr[i-1];
if(arr[i]<0)cout<<0<<' ';
else cout<<arr[i]<<' ';
}
cout<<'\n';
}
//差分 的 模板题 理解 差分和前缀和的关系
//注意 本题要开 long long
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int t=1;
//cin>>t;
//init();
//while(t--)solve1();
while(t--)solve2();
return 0;
}
肖恩的投球游戏

//差分
//肖恩的投球游戏
//https://www.lanqiao.cn/problems/3693/learning/?page=1&first_category_id=1&name=%E8%82%96%E6%81%A9%E7%9A%84%E6%8A%95%E7%90%83%E6%B8%B8%E6%88%8F
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
int const N=1e5+5;
int const M=1e6+1e5;
void init(){
}
ll arr[N];
ll cha[N];
void solve(){
int n,q;
cin>>n>>q;
for(int i=1;i<=n;++i)cin>>arr[i];
int l,r;
ll c;
for(int i=0;i<q;++i){
cin>>l>>r>>c;
cha[l]+=c;
cha[r+1]-=c;
}
//计算差分数组的前缀和
for(int i=1;i<=n;++i){
cha[i]+=cha[i-1];
}
//再算上初始的球的数量
for(int i=1;i<=n;++i){
arr[i]+=cha[i];
cout<<arr[i]<<' ';
}
/*
也可先进行差分的计算
for(int i=1;i<=n;++i){
cha[i]=arr[i]-arr[i-1];
}
统计
for(int i=0;i<q;++i){
cin>>l>>r>>c;
cha[l]+=c;
cha[r+1]-=c;
}
计算
for(int i=1;i<=n;++i){
arr[i]=arr[i-1]+cha[i];
cout<<arr[i]<<' ';
}
*/
cout<<'\n';
}
//差分 的 小变形题
//为了 差分更 舒服
//以后 用两个数组 进行 差分
//算法题一般不会卡空间
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int t=1;
//cin>>t;
//init();
while(t--)solve();
return 0;
}
泡澡

//差分
//泡澡
//https://www.lanqiao.cn/problems/3898/learning/?page=1&first_category_id=1&name=%E6%B3%A1%E6%BE%A1
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
int const N=2e5+5;
int const M=1e6+1e5;
void init(){
}
ll arr[N];
void solve(){
int n,w;
cin>>n>>w;
int s,t;
ll p;
//mi记录最早开始时间, mx记录最晚结束时间
//也可以不用 但需要差分从1到2e5
//比较费时
int mi=0x3f3f3f3f,mx=0;
for(int i=1;i<=n;++i){
cin>>s>>t>>p;
arr[s]+=p;
arr[t]-=p;
mi=min(mi,s);
mx=max(mx,t);
}
//ans记录所需最大热水量
ll ans=0;
for(int i=mi;i<=mx;++i){
arr[i]+=arr[i-1];
ans=max(ans,arr[i]);
}
if(ans<=w){
cout<<"Yes";
}else{
cout<<"No";
}
}
//差分简单题
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int t=1;
//cin>>t;
//init();
while(t--)solve();
return 0;
}
七:二维 前缀和 差分
需要自己画图,三维差分和前缀和也是画图最清晰
肖恩的投球游戏加强版

//二维 前缀和 差分
//肖恩的投球游戏加强版
//https://www.lanqiao.cn/problems/3694/learning/?page=1&first_category_id=1&name=%E8%82%96%E6%81%A9%E7%9A%84%E6%8A%95%E7%90%83%E6%B8%B8%E6%88%8F
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
int const N=1e3+5;
int const M=1e6+1e5;
void init(){
}
ll arr[N][N];
ll barr[N][N];
inline void insert(int x1,int y1,int x2,int y2,ll c){
//差分公式左上角投球
//求前缀和就是x1,y1 到 n,n +投球数c
barr[x1][y1]+=c;
//x1,y2+1 到 n,n -投球数c
barr[x1][y2+1]-=c;
//x2+1,y1 到 n,n -投球数c
barr[x2+1][y1]-=c;
//由于从x2+1,y2+1到 n,n 多-了投球数c
//所以要 + 回来
//自己画图
barr[x2+1][y2+1]+=c;
}
void solve(){
int n,m,q;
cin>>n>>m>>q;
for(int i=1;i<=n;++i){
for(int j=1;j<=m;++j){
cin>>arr[i][j];
//可以预处理差分数组,也可以不处理
//insert(i,j,i,j,arr[i][j]);
}
}
int x1,x2,y1,y2;
ll c;
while(q--){
cin>>x1>>y1>>x2>>y2>>c;
insert(x1,y1,x2,y2,c);
}
//怎么计算前缀和?
//我们差分计算的是 i,j 到 n,n的投球数(本质上是点i,j的影响(投球数))
//前缀和算的就是 1,1 到 i,j 的投球数
//画图 从1,1 到 i,j的投球数
//=点i,j的投球数 + 从1,1 到 i-1,j的投球数 + 从1,1 到 i,j-1的投球数
// - 公共部分(从1,1 到 i-1,j-1的投球数)
for(int i=1;i<=n;++i){
for(int j=1;j<=m;++j){
barr[i][j]+=barr[i-1][j]+barr[i][j-1]-barr[i-1][j-1];
}
}
//前缀和 + 原数组 arr[i][j] 就是 当前点的 投球数
for(int i=1;i<=n;++i){
for(int j=1;j<=m;++j){
arr[i][j]+=barr[i][j];
cout<<arr[i][j]<<' ';
}
cout<<'\n';
}
}
//二维差分的模版题
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int t=1;
//cin>>t;
//init();
while(t--)solve();
return 0;
}
八:离散化
离散化:本质上是一种映射关系:
比如说数组arr[5]={6,11,15,24,13};
通过下标映射 arr[0]=6 通过0索引到了6
数组有一定局限性,还有桶思想的数组映射,也是离散化的一种
由于空间,和增删改查效率的原因,所以
一般离散化通过map来实现
九:贪心
贪心:局部最优 - > 全局最优
每次花费最小 递推到 全部总和花费最小
重要前提是 当前花费最小 不能影响 到后面的运算
如果不满足前提,则贪心就错
前面那个到前缀和的题,就是,当前贪心,一定影响后面的选择
最大数组和(前缀和的题) 不能贪心!
最小化战斗力差距

//贪心
//最小化战斗力差距
//https://www.lanqiao.cn/problems/3412/learning/?page=1&first_category_id=1&name=%E6%9C%80%E5%B0%8F%E5%8C%96
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
int const N=1e5+5;
int const M=1e6+1e5;
void init() {
}
//注意数据范围 wi是1e9的,所以要开longlong
ll a[N];
void solve() {
int n;
cin>>n;
for(int i=1; i<=n; ++i)cin>>a[i];
//排序找两个值差的最小值
sort(a+1,a+1+n);
ll ans=INT_MAX;
for(int i=1; i<n; ++i) {
ans=min(ans,a[i+1]-a[i]);
}
cout<<ans;
}
//题目很明确,有2个容器,必须都非空
//使得两个容器 的 最小值 和 最大值 之间 的 差距 最小
int main() {
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int t=1;
//cin>>t;
//init();
while(t--)solve();
return 0;
}
谈判

//贪心
//谈判
//https://www.lanqiao.cn/problems/545/learning/?page=1&first_category_id=1&name=%E8%B0%88%E5%88%A4
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
int const N=1e5+5;
int const M=1e6+1e5;
void init(){
}
void solve(){
int n;
cin>>n;
//priority_queue 优先队列 本质上是堆 是个容器 没有迭代器
//需用用迭代器的话 用 set 或者 map
//迭代器 可以 配合 auto 更方便
priority_queue<int,vector<int>,greater<int>>pq;
int t;
for(int i=0;i<n;++i){
cin>>t;
pq.push(t);
}
//这题 int 数据就够了
int sum=0;
while(pq.size()>1){
t=pq.top();pq.pop();
t+=pq.top();pq.pop();
sum+=t;
pq.push(t);
}
cout<<sum;
}
//贪心
//虽然当前选择会影响后面选择,但实际上是没影响
//因为 选择 小 大 就是全局最优
// 选择 大 小 就不符合贪心
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int t=1;
//cin>>t;
//init();
while(t--)solve();
return 0;
}
最大的卡牌价值

//贪心
//最大的卡牌价值
//https://www.lanqiao.cn/problems/3250/learning/?page=1&first_category_id=1&name=%E6%9C%80%E5%A4%A7%E7%9A%84%E5%8D%A1%E7%89%8C%E4%BB%B7%E5%80%BC
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
int const N=1e5+5;
int const M=1e6+1e5;
void init(){
}
ll arr[N];
void solve(){
int n,k;
cin>>n>>k;
ll sum=0;
for(int i=1;i<=n;++i)cin>>arr[i],sum+=arr[i];
ll t;
vector<ll>vt;
for(int i=1;i<=n;++i){
cin>>t;
//只有 有价值的 再存数组里
if(t>arr[i]){
vt.push_back(t-arr[i]);
}
}
//数组排序
sort(vt.begin(),vt.end(),greater<ll>());
int index=0;
int size=vt.size();
//k>0 并且 下标小于数组容量
while(k>0 && index<size){
sum+=vt[index];
--k;
++index;
}
cout<<sum;
}
//每次最优 - > 全局最优
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int t=1;
//cin>>t;
//init();
while(t--)solve();
return 0;
}
珠宝的最大交替和

//贪心
//珠宝的最大交替和
//https://www.lanqiao.cn/problems/3791/learning/?page=1&first_category_id=1&name=%E7%8F%A0%E5%AE%9D%E7%9A%84%E6%9C%80%E5%A4%A7%E4%BA%A4%E6%9B%BF%E5%92%8C
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
int const N=1e5+5;
int const M=1e6+1e5;
void init(){
}
ll arr[N];
void solve(){
int n;
cin>>n;
ll sum=0;
ll mi=0x3f3f3f3f3f3f3f3fLL;
ll mx=-1*mi;
for(int i=1;i<=n;++i){
cin>>arr[i];
arr[i]=abs(arr[i]);
if(i&1)sum+=arr[i],mi=min(mi,arr[i]);
else sum-=arr[i],mx=max(mx,arr[i]);
}
if(mx>mi)
sum+=2*(mx-mi);
cout<<sum;
}
//题意只进行一次交换
//故将-的最大值和+的最小值进行替换
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int t=1;
//cin>>t;
//init();
while(t--)solve();
return 0;
}
小蓝的礼物

//贪心
//小蓝的礼物
//https://www.lanqiao.cn/problems/3792/learning/?page=1&first_category_id=1&name=%E5%B0%8F%E8%93%9D%E7%9A%84%E7%A4%BC%E7%89%A9
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
int const N=1e5+5;
int const M=1e6+1e5;
void init(){
}
ll arr[N];
void solve(){
int n;
ll k;
cin>>n>>k;
for(int i=1;i<=n;++i)cin>>arr[i];
sort(arr+1,arr+1+n);
ll sum=0;
//有张向上取整的打折卷
for(int i=1;i<=n;++i){
sum+=arr[i];
if(sum>k){
//sum-arr[i]/2就是向上取整
if(sum-arr[i]/2>k){
cout<<i-1;
return;
}
}
}
cout<<n;
}
//最大化买物品数量
//= 多买价值低的物品
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int t=1;
//cin>>t;
//init();
while(t--)solve();
return 0;
}
鸡哥的购物挑战

//贪心 前缀和
//鸡哥的购物挑战
//https://www.lanqiao.cn/problems/4169/learning/?page=1&first_category_id=1&name=%E9%B8%A1%E5%93%A5%E7%9A%84%E8%B4%AD%E7%89%A9%E6%8C%91%E6%88%98
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
int const N=1e5+5;
int const M=1e6+1e5;
void init(){
}
ll arr[N];
void solve(){
int n;
cin>>n;
for(int i=1;i<=n;++i)cin>>arr[i];
sort(arr+1,arr+1+n,greater<int>());
ll ans=-1;
//前缀和
for(int i=1;i<=n;++i)arr[i]+=arr[i-1];
for(int i=0;i<=n;i+=2){
//arr[0]=0所以严格大于时候 如果ans=0则会直接break
if(arr[i]>ans)ans=arr[i];
else break;
}
cout<<ans;
}
//贪心+前缀和,每次买物品价值最大
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int t=1;
//cin>>t;
//init();
while(t--)solve();
return 0;
}
冒险者公会

//贪心 数据结构
//冒险者公会
//https://www.lanqiao.cn/problems/3611/learning/?page=1&first_category_id=1&name=%E5%86%92%E9%99%A9%E8%80%85%E5%85%AC%E4%BC%9A
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
int const N=1e3+5;
//用multiset或者map来存每个能力值
multiset<int> mst;
//记录第 i 村庄 第 j 个委托 的 难度
int cost[N][N];
void solve() {
int n,m;
int mx=0,k,t;
cin>>n>>m;
for(int i=0; i<n; ++i){
cin>>t;
mst.insert(t);
}
for(int i=0; i<m; ++i) {
cin>>k;
mx=max(mx,k);
for(int j=0; j<k; ++j) {
cin>>cost[i][j];
}
//排序未来对齐 每个冒险者 去 每个村子 解决 第 j 委托最难的任务
sort(cost[i],cost[i]+k,greater<int>());
}
int sum=0,mxcost=0;
for(int i=0; i<mx; ++i) {
mxcost=0;
//记录 每个村子 第 i 任务 最难的难度
for(int j=0;j<m;++j){
mxcost=max(mxcost,cost[j][i]);
}
//只要有一个能力 大于等于 最大难度 就行
// lowe_bound底层是二分查找,能更快
auto number=lower_bound(mst.begin(),mst.end(),mxcost);
if(number==mst.end()){
//说明没有满足条件的
cout<<-1;
return;
}
sum+=*number;
//由于每个勇者只能用一次,所以要erase掉
//用map或者multiset
mst.erase(number);
}
cout<<sum;
}
//贪心 : 每论 委托 恰好 勇者能力值 大于等于 难度
int main() {
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int t=1;
//cin>>t;
//init();
while(t--)solve();
return 0;
}
明日方舟大作战

//贪心 动态规划 01背包
//明日方舟大作战
//https://www.lanqiao.cn/problems/4049/learning/?page=1&first_category_id=1&name=%E6%98%8E%E6%97%A5%E6%96%B9%E8%88%9F%E5%A4%A7%E4%BD%9C%E6%88%98
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
int const N=1e4+5;
int const M=1e6+1e5;
void init() {
}
//int 取值范围就够了
//最大花费为 i 下的 最大攻击力 dp[i]
int dp[N];
void solve() {
int n,m,b;
cin>>n>>m>>b;
int attack,cost;
for(int i=1; i<=n; ++i) {
cin>>attack>>cost;
for(int j=b; j>=cost; --j) {
dp[j]=max(dp[j],dp[j-cost]+attack);
}
}
int life,mx=0;
for(int i=1; i<=m; ++i) {
cin>>life;
mx=max(mx,life);
}
if(dp[b]==0)cout<<-1;
//else cout<<(mx+dp[b]-1)/dp[b];
else cout<<ceil((double)mx / dp[b] );
}
//这题期初看的是完全做不了
//以为干员的花费是每轮的 但实质上是招募关系
//就是只要使用该干员 可以一直干活
//结果就很明显了 最大花费下的最大攻击力
//dp[i] 最大花费为 i 下的 最大攻击力 dp[i]
//dp从1开始需要开二维
//dp从 b开始就是最大容量
//dp从后往前可以避免前者dp改变后者dp
//dp 前者改后者 是完全背包
//前者不改变后者是 01背包
//开二维dp的话 从前往后dp也行
//最后算一下轮数就行
int main() {
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int t=1;
//cin>>t;
//init();
while(t--)solve();
return 0;
}
体育健将

//贪心 特判
//体育健将
//https://www.lanqiao.cn/problems/3875/learning/?page=1&first_category_id=1&problem_id=3875
#include <bits/stdc++.h>
using namespace std;
using ll=long long;
int const N=2e5+10;
pair<ll,ll> a[N];
//这题就贪心+特判就行
int main()
{
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int n,k;
cin>>n>>k;
for(int i=1;i<=n;++i)cin>>a[i].first;
for(int i=1;i<=n;++i)cin>>a[i].second;
sort(a+1,a+1+n,[](pair<ll,ll> x,pair<ll,ll> y){
return x.first+x.second<y.first+y.second;
});
ll sum=0;
int ans=0,index=0;
for(int i=1;i<=n;++i){//这里是贪心
sum+=a[i].first;
if(sum>k){
sum-=a[i].first;
index=i;
break;
}
sum+=a[i].second;
}
ans=index-1;
for(int i=index+1;i<=n;++i){//开始特判,找一个参赛时间短的,最多只有一个
if(sum+a[i].first<=k){
++ans;
break;
}
}
cout<<ans;
return 0;
}
十:双指针
回文判定

//双指针
//回文判定
//https://www.lanqiao.cn/problems/1371/learning/?page=1&first_category_id=1&name=%E5%9B%9E%E6%96%87%E5%88%A4%E5%AE%9A
/*
条件:有序:非递增 非递减
1:对撞指针:在数组的两端(用于:区间值)
2:快慢指针:在数组的同一段,走的速度不一致(用于:链表倒数第k个元素,环形链表)
*/
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
int const N=1e5+5;
int const M=1e6+1e5;
void init(){
}
void solve(){
string s;
cin>>s;
int len=s.length();
int left=0,right=len-1;
while(left<right){
if(s[left]!=s[right]){
cout<<"N";
return;
}
++left;
--right;
}
cout<<"Y";
}
//双指针模板题
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int t=1;
//cin>>t;
//init();
while(t--)solve();
return 0;
}
上面的题也可以通过string t=s; reverse(s.begin(),s.end()); if(t==s)cout<<"Y";else cout<<"N";
美丽的区间

//双指针 前缀和
//美丽的区间
//https://www.lanqiao.cn/problems/1372/learning/?page=1&first_category_id=1&name=%E7%BE%8E%E4%B8%BD
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
int const N=1e5+5;
int const M=1e6+1e5;
void init(){
}
//本题int够存
int arr[N];
int prv[N];
//solve1是通过前缀和做的
void solve1(){
int n,s;
cin>>n>>s;
for(int i=1;i<=n;++i){
cin>>arr[i];
prv[i]=prv[i-1]+arr[i];
}
int len=n+1;
int l=1,r=1;
while(l<=r && r<=n){
while(r<=n && prv[r]-prv[l-1]<s){
++r;
}
if(r<=n && prv[r]-prv[l-1]>=s){
len=min(len,r-l+1);
}
++l;
}
if(len==n+1)cout<<0;
else cout<<len;
}
//solve2通过变量sum来实时记录值
void solve2(){
int n,s;
cin>>n>>s;
for(int i=1;i<=n;++i)cin>>arr[i];
int sum=0;
int r=1;
int len=n+1;
for(int i=1;i<=n;++i){
while(sum<s && r<=n){
sum+=arr[r];
++r;
}
if(sum>=s){
//本来是r-i+1的,但sum+=arr[r]后 ++r,所以范围是[i,r)
len=min(len,r-i);
}
if(r==n+1)break;
sum-=arr[i];
}
if(len==n+1)cout<<0;
else cout<<len;
}
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int t=1;
//cin>>t;
//init();
//while(t--)solve1();
while(t--)solve2();
return 0;
}
挑选子串

//双指针
//挑选子串
//https://www.lanqiao.cn/problems/1621/learning/?page=1&first_category_id=1&name=%E6%8C%91%E9%80%89%E5%AD%90%E4%B8%B2
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
int const N=2e3+5;
int const M=1e6+1e5;
void init(){
}
int a[N];
void solve(){
int n,m,k;
cin>>n>>m>>k;
for(int i=1;i<=n;++i)cin>>a[i];
int r=0,sum=0;
int ans=0;
for(int i=1;i<=n;++i){
//越界访问r<=n,如果空间没给够必须有,否则越界error
while(sum<k && r<=n){
++r;
if(a[r]>=m)++sum;
}
//这里不确定是n-r还是n-r+1的可以将ans输出看看情况
if(sum>=k)ans+=n-r+1;
//cout<<ans<<'\n';
sum-=(a[i]>=m);
}
cout<<ans;
}
//数据范围也很明显2e3,说明最大是O(n^2)的
//
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int t=1;
//cin>>t;
//init();
while(t--)solve();
return 0;
}
聪明的小羊肖恩

//双指针 排序
//聪明的小羊肖恩
//https://www.lanqiao.cn/problems/3695/learning/?page=1&first_category_id=1&problem_id=3695
#include <bits/stdc++.h>
using namespace std;
using ll=long long;
int const N=2e5+10;
int a[N];
int n;
ll work(int num){
int l=1,r=n;
ll ans=0;
while(l<r){
while(l<r&&a[l]+a[r]>num){
--r;
}
ans+=r-l;
++l;
}
return ans;
}
int main()
{
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int l,r;
cin>>n>>l>>r;
for(int i=1;i<=n;++i){
cin>>a[i];
}
sort(a+1,a+1+n);
//由于统计 i,j的对数 可以排序
//L<=ai+aj<=R可以转化为 L<=ai+aj ai+aj<=R
//根据容斥ai+aj<=R的临界值数量-L<=ai+aj的临界值数量+1
//就是我们要求的,要么写俩双指针
//要么写一个双指针,传参改成l-1,这样俩临界值错开了,直接-不用+1
cout<<work(r)-work(l-1)<<'\n';
return 0;
}
/*
//或者暴力美学
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
const int N=2e5+5;
ll a[N];
int main()
{
ll x,y,z;cin>>x>>y>>z;
for(ll i=1;i<=x;++i)cin>>a[i];
sort(a+1,a+1+x);
ll ans=0,left=0,right=0;
for(int i=1;i<=x;++i)
{
left=y-a[i];
right=z-a[i];
ans+=upper_bound(a+i+1,a+x+1,right)-lower_bound(a+1+i,a+1+x,left);
}
cout<<ans;
return 0;
}
*/
神奇的数组

//双指针
//神奇的数组
//https://www.lanqiao.cn/problems/3000/learning/?page=1&first_category_id=1&name=%E7%A5%9E%E5%A5%87%E7%9A%84%E6%95%B0%E7%BB%84
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
int const N=2e5+5;
int const M=1e6+1e5;
void init() {
}
ll prv[N];
ll pre[N];
void solve1() {
int n;
ll num;
cin>>n;
for(int i=1; i<=n; ++i) {
cin>>num;
prv[i]=prv[i-1]+num;
pre[i]=pre[i-1]^num;
}
ll ans=0;
for(int i=1; i<=n; ++i) {
for(int j=i; j<=n; ++j) {
if((prv[j]-prv[i-1])==(pre[j]^pre[i-1]))++ans;
else break;
}
}
cout<<ans;
}
void solve2() {
int n;
ll num;
cin>>n;
for(int i=1; i<=n; ++i) {
cin>>num;
prv[i]=prv[i-1]+num;
pre[i]=pre[i-1]^num;
}
ll ans=0;
int l=1,r=1;
while(l<=n&&r<=n) {
if((prv[r]-prv[l-1])==(pre[r]^pre[l-1])) {
ans+=r-l+1;
++r;
} else ++l;
}
cout<<ans;
}
//这题乍眼一看,枚举左右端点的,因为连续
//但由于前缀和和异或前缀和的连续性,
//导致,如果l到l+1不满足前缀和==异或前缀和
//那么,从l到l+2~r的范围绝对不满足 前缀和==异或前缀和
//时间复杂度从 O(n^2) 降 到O(n)
//solve1有一个测试用例没过去,超时了
//solve2全通过了
//思考ans+=r-l+1为什么比++ans快?
//以及这种算某一位数的贡献的考点
int main() {
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int t=1;
//cin>>t;
//init();
//while(t--)solve1();
while(t--)solve2();
return 0;
}
/*
solve1有一个测试用例没过去,超时了
solve2全通过了
思考ans+=r-l+1为什么比++ans快?
以及这种算某一位数的贡献的考点
*/
十一:二分
二分查找数组元素(模板):二分查找数组元素

//二分
//二分查找数组元素
//https://www.lanqiao.cn/problems/1389/learning/?page=1&first_category_id=1&name=%E4%BA%8C%E5%88%86%E6%9F%A5%E6%89%BE
/*
(一)套路:
1整数二分
2浮点二分(实数二分)
3二分答案(常考)
4二分答案条件:单调性,当前数据满足条件,那么后面的任意一个数据满足条件,或者当前数满足条件,后面的数据不满足条件
*/
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
int const N=1e5+5;
int const M=1e6+1e5;
void init() {
}
void solve() {
int nums[205];
for(int i=0; i<200; ++i)
nums[i]=4*i+6;
int t;
cin>>t;
int num=(lower_bound(nums,nums+200,t)-nums);
cout<<num;
}
//
int main() {
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int t=1;
//cin>>t;
//init();
while(t--)solve();
return 0;
}
跳石头:跳石头

//二分
//跳石头
//https://www.lanqiao.cn/problems/364/learning/?page=1&first_category_id=1&name=%E8%B7%B3%E7%9F%B3%E5%A4%B4
#include <bits/stdc++.h>
using namespace std;
using ll=long long;
int const N=5e4+5;
int pos[N];
int L,n,m;
bool check(int way){
int cnt=0,prv=0;
for(int i=1;i<=n;++i){
//当前位置pos[i] - 前一个石头的位置prv = 距离
//如果距离小于答案要求的最小距离
//说明需要将石头搬走
if(pos[i]-prv<way){
++cnt;
}//否则说明该石头不需要搬走,同时,前一个石头的位置变成当前位置了
else{
prv=pos[i];
}
}
if(cnt<=m)return true;
return false;
}
void solve(){
cin>>L>>n>>m;
//题目说了石头顺序是按照从小到大
for(int i=1;i<=n;++i)cin>>pos[i];
//最少距离是1,最大距离是l
int l=1,r=L,mid;
//二分距离(答案)
int ans=1;
while(l<=r){
//用(l+r)/2部分题可能l+r越界
mid=l+((r-l)>>1);
if(check(mid)){
//但凡怕错mid和while(l<=r)的,可以用ans记录答案
ans=mid;
l=mid+1;
}else r=mid-1;
}
cout<<ans;
}
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int t=1;
//cin>>t;
while(t--)solve();
return 0;
}
肖恩的苹果林:肖恩的苹果林

//二分
//肖恩的苹果林
//https://www.lanqiao.cn/problems/3683/learning/?page=1&first_category_id=1&name=%E8%8B%B9%E6%9E%9C%E6%9E%97
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
int const N=1e5+5;
int const M=1e6+1e5;
int n,m;
int pos[N];
void init() {
}
bool check(int way){
//默认第一个位置直接种
//因为每次只考虑当前点和前一个点的位置
//思想是贪心
int cnt=1,prv=pos[1];
for(int i=2;i<=n;++i){
if(pos[i]-prv>=way){
prv=pos[i];
++cnt;
}
}
if(cnt>=m)return true;
return false;
}
void solve() {
cin>>n>>m;
for(int i=1;i<=n;++i)cin>>pos[i];
//由于题目没有明确说明递增,所以要排序
sort(pos+1,pos+1+n);
//题目没表面最大距离,但最大是xi<=1e9
int l=0,r=1e9+7,mid,ans;
while(l<=r){
mid=l+((r-l)>>1);
if(check(mid)){
ans=mid;
l=mid+1;
}else r=mid-1;
}
cout<<ans;
}
//
int main() {
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int t=1;
//cin>>t;
//init();
while(t--)solve();
return 0;
}
肖恩的乘法表:

//二分
//肖恩的乘法表题目
//https://www.lanqiao.cn/problems/3404/learning/?page=1&first_category_id=1&name=%E4%B9%98%E6%B3%95%E8%A1%A8
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
int const N=5e5+5;
int const M=1e6+1e5;
ll n,m,k;
void init() {
}
void solve() {
cin>>n>>m>>k;
ll l=0,r=n*m,mid,ans;
while(l<=r){
//判断期望mid下,之前小于等于mid的数的个数是否小于k(正好是k-1个数)
//满足小于k,那么mid这个数是第k个元素
mid=l+((r-l)>>1);
ll cnt=0;
for(ll i=1;i<=n;++i)cnt+=min(mid/i,m);
if(cnt<k){
ans=mid+1;
l=mid+1;
}else r=mid-1;
}
cout<<ans;
}
//如果直接判断矩形中的顺序,反而不好算
//但满足二分的两个特性1:单调性2:当前条件满足,后面的每个数都满足条件
//考虑二分答案,l和r确定下来在看题目条件第k大(前k-1小于当前数)
//所以二分答案
int main() {
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int t=1;
//cin>>t;
//init();
while(t--)solve();
return 0;
}
可凑成的最大花束数

//二分
//可凑成的最大花束数
//https://www.lanqiao.cn/problems/3344/learning/?page=1&first_category_id=1&name=%E5%8F%AF%E5%87%91%E6%88%90%E7%9A%84%E6%9C%80%E5%A4%A7%E8%8A%B1%E6%9D%9F%E6%95%B0
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
int const N=2e5+5;
int const M=1e6+1e5;
int n,k;
int hua[N];
void init() {
}
bool check(ll mid) {
ll cnt=0;
//cnt记录总贡献的花数量
//min(hua[i],mid)
//由于每花束的任意两朵颜色不同
//所以每朵花最大贡献mid个,最少贡献全部花
for(int i=1; i<=n; ++i)cnt+=min(1LL*hua[i],mid);
//判断当前花束能否凑出来k束
//由于用除法导致精度问题,所以用乘法
if(cnt>=mid*k)return true;
return false;
}
void solve() {
cin>>n>>k;
ll sum=0;
//sum计算总花数
for(int i=1; i<=n; ++i)cin>>hua[i],sum+=hua[i];
//花束下限0,上限sum/k,+1都行,含除法的情况下,比赛的时候最好加上
ll l=0,r=sum/k+1,mid,ans;
while(l<=r) {
mid=l+((r-l)>>1);
if(check(mid)) {
ans=mid;
l=mid+1;
} else r=mid-1;
}
cout<<ans;
}
//明显的二分题:1单调性2当前数据满足条件,后面数据一定满足条件
int main() {
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int t=1;
//cin>>t;
//init();
while(t--)solve();
return 0;
}
最大通过数

//二分 前缀和
//最大通过数
//https://www.lanqiao.cn/problems/3346/learning/?page=1&first_category_id=1&name=%E6%9C%80%E5%A4%A7%E9%80%9A%E8%BF%87%E6%95%B0
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
int const N=2e5+5;
int const M=1e6+1e5;
void init() {
}
ll a[N];
ll b[N];
void solve() {
int n,m,k;
cin>>n>>m>>k;
for(int i=1; i<=n; ++i)cin>>a[i],a[i]+=a[i-1];
for(int i=1; i<=m; ++i)cin>>b[i],b[i]+=b[i-1];
int ans=0;
for(int i=0; i<=n; ++i) {
int cnt=k-a[i];
int pos=upper_bound(b+1,b+1+m,cnt)-b;
//必须使用upper_bound是大于,因为只有0~pos-1满足<=cnt
//lower_bound是大于等于,无法判断是大于 or 小于
--pos;
//--pos后就是-1~pos-1满足<=cnt花费
if(pos<0)pos=0;
//由于-1的非法性,所以pos=0
if(cnt>=0)
ans=max(i+pos,ans);
else break;
}
cout<<ans;
}
//二分的两个特点都满足
int main() {
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int t=1;
//cin>>t;
//init();
while(t--)solve();
return 0;
}
妮妮的月饼工厂:妮妮的月饼工厂

//二分
//妮妮的月饼工厂
//https://www.lanqiao.cn/problems/3990/learning/?page=1&first_category_id=1&problem_id=3990
#include <bits/stdc++.h>
using namespace std;
int const N=1e5+5;
int a[N];
int n,k;
bool check(int mid){
int sum=0;
for(int i=1;i<=n;++i){
sum+=a[i]/mid;
}
if(sum>=k)return true;
return false;
}
int main()
{
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>n>>k;
for(int i=1;i<=n;++i)cin>>a[i];
int l=1,r=1e9+7;
while(l<=r){//由于check里是/mid,除0会出错,l初值最好为1,最后特判
int mid=(l+r)>>1;
if(check(mid))l=mid+1;
else r=mid-1;
}
if(r==0)cout<<-1;
else cout<<r;
return 0;
}
基德的神秘冒险:基德的神秘冒险

//二分 前缀和
//基德的神秘冒险
//https://www.lanqiao.cn/problems/4860/learning/?page=1&first_category_id=1&problem_id=4860
#include <bits/stdc++.h>
using namespace std;
using ll=long long;
int const N=3e5+5;
int a[N];
ll b[N];
int main()
{
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int n,q;
cin>>n>>q;
for(int i=1;i<=n;++i)cin>>a[i];
sort(a+1,a+1+n);
for(int i=1;i<=n-2;++i){
b[i]=1LL*(n-i)*(n-i-1)/2;
b[i]+=b[i-1];
}
ll k;
ll index;
while(q--){
cin>>k;
index=lower_bound(b+1,b+1+n-2,k)-b;
cout<<a[index]<<'\n';
}
return 0;
}
十二:滑动窗口 单调栈
四元组问题:四元组问题

//滑动窗口 单调栈
//四元组问题
//https://www.lanqiao.cn/problems/3416/learning/?page=1&first_category_id=1&problem_id=3416
#include <bits/stdc++.h>
using namespace std;
int const N=5e5+50;
int arr[N];
int miarr[N];
int main()
{
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int n;
cin>>n;
for(int i=1;i<=n;++i)cin>>arr[i];
miarr[n+1]=0x3f3f3f3f;
//滑动窗口,算d
for(int i=n;i>0;--i)miarr[i]=min(miarr[i+1],arr[i]);
int k=INT_MIN;//a
stack<int>st;
for(int i=1;i<=n;++i){//ai=c
if(arr[i]<k){//c<a
if(arr[i]>miarr[i]){//c>d
cout<<"YES\n";
return 0;
}
}
while(!st.empty()&&st.top()<arr[i]){//栈里存b,最大的,次最大是k=a,确保次最大比枚举的c大就行
k=max(k,st.top());//k=a
st.pop();
}
st.push(arr[i]);//b
}
cout<<"NO\n";
return 0;
}
十三:快速幂
快速幂:快速幂

//快速幂
//快速幂
//https://www.lanqiao.cn/problems/1514/learning/?page=1&first_category_id=1&problem_id=1514
#include <bits/stdc++.h>
using namespace std;
using ll=long long;
//快速幂
ll b,p,k;
ll ksm(ll a,ll b){
ll res=1;
while(b){
if(b&1)res=res*a%k;
a=a*a%k;
b>>=1;
}
return res;
}
int main()
{
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>b>>p>>k;
cout<<ksm(b,p)<<'\n';
return 0;
}
十四:LCA(倍增)
LCA也可以通过树链剖分做,后面可能讲到
最近公共祖先LCA查询:最近公共祖先LCA查询

//LCA
//最近公共祖先LCA查询
//https://www.lanqiao.cn/problems/4385/learning/?page=1&first_category_id=1&tag_relation=intersection&problem_id=4385
#include <bits/stdc++.h>
using namespace std;
int const N=1e5+5;
vector<int> mp[N];
int deep[N];//记录深度
int dis[N][20];//第i位置跳2的j次方到dis[i][j]
void bfs(int x,int fa){
deep[x]=deep[fa]+1;
dis[x][0]=fa;//跳2的0次方,就是1次到fa
for(int i=1;i<20;++i){//先跳一半,再跳另一半
dis[x][i]=dis[dis[x][i-1]][i-1];
}
for(int pos:mp[x]){
if(pos==fa)continue;//这图是双向图,避免重复搜索
bfs(pos,x);
}
}
int lca(int x,int y){
if(deep[x]>deep[y])swap(x,y);//先吧x节点和y节点跳到同一个深度(高度)
for(int i=19;i>=0;--i){//一定是从大变小的跳,如果是从小到大跳不过去(有概率)
if(deep[dis[y][i]]>=deep[x]){
y=dis[y][i];
}
}
if(x==y)return x;//如果x和y相等说明祖先了
for(int i=19;i>=0;--i){//一定是从大变小的跳,如果是从小到大跳不过去(有概率)
if(dis[x][i]!=dis[y][i]){//不等就同时跳
x=dis[x][i];
y=dis[y][i];
}
}
return dis[x][0];//再跳一步就相等了
}
int main()
{
int n;
cin>>n;
int u,v;
for(int i=1;i<n;++i){
cin>>u>>v;
mp[u].push_back(v);//存图没啥可说的
mp[v].push_back(u);
}
deep[1]=1;
bfs(1,0);//深度,理论上是dfs的,改个名字而已
int q;
cin>>q;
int a,b;
while(q--){
cin>>a>>b;
cout<<lca(a,b)<<'\n';
}
return 0;
}
十五:倍增
如今仍是遥远的理想之城1
//倍增
//如今仍是遥远的理想之城1
/*
魔法师梅林在卡美洛召唤了 N 个传送阵(编号1~N),每个传送阵都能传送到另一个编号为 ai的传送阵。
现在为了测试传送阵的连通性,梅林将关芙丢进了 1号传送阵进行飞次传送。问你,在传送结束后,芙芙此时在第几个传送阵。
输入格式
第一行输入 2 个正整数 N,k,表示传送阵的数量以及传送次数。
第二行输入 N 个数字,表示第之个传送阵可以传送至ai 位置。
输出格式
输出一行一个整数,表示传送 ん次后关芙所在的位置
样例输入
3
样例输出
说明
样例含义为:1→2→3→2→3。
评测数据规模
[≤ N ≤ 2 x 10',1 ≤ ai≤ N,1 ≤ k≤ 1018
运行限制语言C++
最大运行时间 最大运行内存 1s 256M
*/
#include <bits/stdc++.h>
using namespace std;
using ll=long long;
int const N=2e5+5;
int fa[N][61];
ll ksm(int x){
ll res=1;
ll base=2;
while(x){
if(x&1)res*=base;
base*=base;
x>>=1;
}
return res;
}
int main()
{
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int n;
ll k;
cin>>n>>k;
for(int i=1;i<=n;++i)cin>>fa[i][0];
for(int j=1;j<=60;++j){//1e19是longlong 8字节 2的64次方-1 少个10 3次方,60够用了
for(int i=1;i<=n;++i){
fa[i][j]=fa[fa[i][j-1]][j-1];
}
}
int pos=1;
int nex;
while(k!=0){//k一定可以用二进制表示
nex=log(k)/log(2LL);//每次能走2的nex次方
pos=fa[pos][nex];
k-=ksm(nex);//知道把k减到0
}
cout<<pos;
return 0;
}
//倍增
//如今仍是遥远的理想之城1
/*
魔法师梅林在卡美洛召唤了 N 个传送阵(编号1~N),每个传送阵都能传送到另一个编号为 ai的传送阵。
现在为了测试传送阵的连通性,梅林将关芙丢进了 1号传送阵进行飞次传送。问你,在传送结束后,芙芙此时在第几个传送阵。
输入格式
第一行输入 2 个正整数 N,k,表示传送阵的数量以及传送次数。
第二行输入 N 个数字,表示第之个传送阵可以传送至ai 位置。
输出格式
输出一行一个整数,表示传送 ん次后关芙所在的位置
样例输入
3
样例输出
说明
样例含义为:1→2→3→2→3。
评测数据规模
[≤ N ≤ 2 x 10',1 ≤ ai≤ N,1 ≤ k≤ 1018
运行限制语言C++
最大运行时间 最大运行内存 1s 256M
*/
#include <bits/stdc++.h>
using namespace std;
using ll=long long;
int const N=2e5+5;
int a[N];
int prv[N];
int main()
{
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int n;
ll k;
cin>>n>>k;
for(int i=1;i<=n;++i)cin>>a[i];
int cnt=0;
int pos=1;
while(k){
--k;//跳一次
pos=a[pos];//跳到pos
++cnt;//跳的次数
if(prv[pos])k%=(cnt-prv[pos]);
//上次跳过这个点,这次是第二次跳到这里了,走了cnt-prv[pos]步
//每这么多步的倍数,位置不变,通过%来缩短距离
prv[pos]=cnt;
}
cout<<pos;
return 0;
}
数的变换
//倍增 极限
//数的变换
/*
数的变换
问题描述
给的 A,B,C,Q,进行以下操作 Q 次:
将A变为[A/B」(向下取整)+C.
请你输出 Q 次操作后 A 的权值。
输入格式
第一行包含 4个正整数 A,B,C,Q.
输出格式
输出共 1行,包含1个整数,表示最终答案。
样例输入
5 2 1 1
样例输出
3
*/
#include <bits/stdc++.h>
using namespace std;
using ll=long long;
int const N=2e5+5;//求极限是2e5
ll arr[N][25];
int main()
{
ll a,b,c,d;
cin>>a>>b>>c>>d;
if(b==1){
cout<<a+c*d<<'\n';
return 0;
}
for(int i=0;i<N;++i)arr[i][0]=i/b+c;//预处理0次方的值
for(int j=1;j<25;++j){//倍增的套路
for(int i=0;i<N;++i){
arr[i][j]=arr[arr[i][j-1]][j-1];
}
}
int index=0;
while(d){//将d转成二进制的形式
if(d&1)a=arr[a][index];
++index;
d>>=1;
}
cout<<a<<'\n';//时间复杂度Nlog(N)
//时间主要在倍增处理N个数,前面是N,后面这个while就log(d)
return 0;
}
/*
本题还有三个思考
1为什么不怕lower_bound越界和upper_bound越界?
2怎么防范越界问题??
3什么时候要排序???
*/
十六:位运算
位运算:位运算

//位运算
//二进制中 1 的个数
//https://www.lanqiao.cn/problems/1331/learning/?page=1&first_category_id=1&problem_id=1331
#include <bits/stdc++.h>
using namespace std;
int main()
{
unsigned int t;cin>>t;
//t=abs(t);错的
//t=t+((t<0)?4294967296:0);
//这个刷题网页 对 long long 不太兼容
//我用 long long 也是没过
int ans=0;
while(t)
{
ans+=t&1;
t>>=1;
}
cout<<ans;
return 0;
}
区间或:区间或

//前缀和 位运算
//区间或
//https://www.lanqiao.cn/problems/3691/learning/?page=1&first_category_id=1&problem_id=3691
#include <bits/stdc++.h>
using namespace std;
int const N=1e5+10;
int dp[N][21];//第 i 位置(ai) 二进制第j 位的次数
void getdata(int pos,int num){//把num拆成二进制
int index=0;
while(num){
if(num&1)++dp[pos][index];
num>>=1;
++index;
}
}
int main()
{
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int n,q;
cin>>n>>q;
int t;
for(int i=1;i<=n;++i){
cin>>t;
getdata(i,t);
}
//预处理
for(int i=1;i<=n;++i){
for(int j=0;j<21;++j){
dp[i][j]+=dp[i-1][j];//算二进制j位置的次数的前缀和
}
}
int l,r;
while(q--){
cin>>l>>r;
int num=0;
for(int j=0;j<21;++j){
if(dp[r][j]>dp[l-1][j]){//前缀和作差也可以,看该位置是否+1
num+=(1<<j);
}
}
cout<<num<<'\n';
}
return 0;
}
异或森林:异或森林

//前缀异或和 差分 容斥
//异或森林
//https://www.lanqiao.cn/problems/3400/learning/?page=1&first_category_id=1&problem_id=3400
#include <bits/stdc++.h>
using namespace std;
int const N=1e5+5;//大于16383就行
int a[N];
int cnt[N];
//异或的性质可以前缀异或和
void solveA(){//暴力+思维,思维的点在一个数的因数是偶数个,那么他一定不是平方数(a*a!=num)
int n;
cin>>n;
for(int i=1;i<=n;++i){
cin>>a[i];
a[i]^=a[i-1];
}
int ans=0;
for(int i=1;i<=n;++i){
for(int j=i;j<=n;++j){
int num=a[j]^a[i-1];
if(num==0)continue;
int sq=(int)sqrt(num);
if(sq*sq!=num){
++ans;
}
}
}
cout<<ans;
}
void sovleB(){
//枚举每个数,如果存在非完全平方数,那么这个数出现的次数就是满足条件的
int n;
cin>>n;
for(int i=1;i<=n;++i){
cin>>a[i];
a[i]^=a[i-1];
}
int ans=n*(n+1)/2;//现在反过来,用总可能数减去不满足条件的
cnt[0]=1;//0也是一个不满足条件的个数为1
for(int i=1;i<=n;++i){
for(int j=0;j<=200;++j){//枚举平方数,1e4本来需要100就行,但异或前缀和的ai最大可以到2倍,2e4
int sq=j*j;
ans-=cnt[sq^a[i]];//-之前结果为b[i]=sq^a[i]的次数
}
++cnt[a[i]];//添进去
}
cout<<ans;
}
int getsum(){
int ans=0;
int r=1e4;
for(int i=1;i<=r;++i){
ans=ans|i;
}
return ans;
}
int main()
{
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
//cout<<getsum()<<'\n';//16383
//solveA();
sovleB();
return 0;
}
最小的或运算:最小的或运算
//位运算
//最小的或运算
//https://www.lanqiao.cn/problems/4900/learning/?page=1&first_category_id=1&problem_id=4900
#include <bits/stdc++.h>
using namespace std;
using ll=long long;
int main()
{
ll a,b;
cin>>a>>b;
ll sum=a|b;
ll temp=sum&a&b;
//cout<<sum-temp;
//想的快可以直接异或
cout<<(a^b);
return 0;
}
简单的异或难题:简单的异或难题

//位运算 异或前缀和
//简单的异或难题
//https://www.lanqiao.cn/problems/3217/learning/?page=1&first_category_id=1&problem_id=3217
#include <bits/stdc++.h>
using namespace std;
int const N=1e5+5;
int a[N];//这题关键是奇数和偶数的差别,就再异或一次就行
int main()
{
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int n,m;
cin>>n>>m;
for(int i=1;i<=n;++i){
cin>>a[i];
a[i]^=a[i-1];
}
int l,r;
while(m--){
cin>>l>>r;
cout<<(a[r]^a[l-1])<<'\n';
}
return 0;
}
出列:出列

//位运算
//出列
//https://www.lanqiao.cn/problems/3223/learning/?page=1&first_category_id=1&problem_id=3223
#include <bits/stdc++.h>
using namespace std;
int main()
{
int a;
cin>>a;
int t=a;
int num=1;
while(num<=t){
a>>=1;
num<<=1;
}
cout<<(num>>=1);
return 0;
}
小蓝学位运算:小蓝学位运算

//位运算 暴力
//小蓝学位运算
//https://www.lanqiao.cn/problems/3220/learning/?page=1&first_category_id=1&problem_id=3220
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
ll const MOD=1e9+7;
int const N=1e6+10;
ll a[N];
int main()
{
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int n;
cin>>n;
for(int i=1;i<=n;++i){
cin>>a[i];
a[i]^=a[i-1];
}
ll ans=1;
ll t;
for(int i=1;i<=n;++i){
for(int j=i;j<=n;++j){
t=(a[j]^a[i-1]);
if(t==0){
cout<<0;
return 0;
}
ans=(ans*t)%MOD;
}
}
cout<<ans;
return 0;
}
位移:位移

//位运算
//位移
//https://www.lanqiao.cn/problems/3261/learning/?page=1&first_category_id=1&problem_id=3261
#include<bits/stdc++.h>
using namespace std;
/*
在主串中找子串
//将num的二进制数从低到高,连续的0删去(低位置的0),高位置一定是1
//获取子串和主串的字符串(形式都是1...1)
//将s1转化为s2,就是找s1中s2是否出现,因为在s1中未出现的s2后面多余的可以通过移动删掉
*/
string change(int num){
string ch;
while(num){
if(num&1)break;//将num的二进制数从低到高,连续的0删去,
num>>=1;
}
while(num){//获取子串和主串首位非0的字符串
ch+=(num&1?'1':'0');
num>>=1;
}
return ch;
}
void solve(){
int m,n;cin>>m>>n;
string s1=change(m),s2=change(n);
//将s1转化为s2,就是找s1中s2是否出现,因为在s1中未出现的s2后面多余的可以通过移动删掉
if(s1.find(s2)!=-1)cout<<"Yes"<<'\n';
else cout<<"No"<<'\n';
}
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int t;cin>>t;
while(t--)solve();
return 0;
}
十七:数位DP
笨笨的机器人:笨笨的机器人

//数位DP
//笨笨的机器人
//https://www.lanqiao.cn/problems/3262/learning/?page=1&first_category_id=1&problem_id=3262
#include <bits/stdc++.h>
using namespace std;
//这题要么dfs,要么dp
//属于偏位运算的 数位dp
int const N=16;
int a[N];
int main()
{
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int n;
cin>>n;
for(int i=1;i<=n;++i){
cin>>a[i];
}
int sum=(1<<n)-1;//2的n次方是1左移n位
//-1是0000到1111,0表示该位置数要+,1表示该位置需要减
//就是暴力枚举每种情况
int cnt=0,num=0;
for(int i=0;i<=sum;++i){
num=0;
for(int j=1;j<=n;++j){
if((i>>(j-1))&1)num-=a[j];//也可以if((i>>(j-1))&1)
else num+=a[j];
}//每次计算可以(+7-a[j])%7,也可以最后一步num%7
if(num%7==0)++cnt;
}
double ans=cnt*1.0/(1<<n);
ans=round(ans*10000)/10000;
cout<<fixed<<setprecision(4)<<ans;
return 0;
}
837

被折叠的 条评论
为什么被折叠?



