C++基础算法

有目录,想做什么类型的题可以跳转

目录

一:模拟 暴力 枚举 构造 思维

特别数的和

反倍数

找到最多的数

小蓝的漆房

扫雷

小蓝和小桥的挑战

灌溉

回文日期

回文日期2

DNA序列修正

无尽的石头无尽的石头:无尽的石头

        小浩的ABC:小浩的ABC

        小蓝找答案:小蓝找答案

        小蓝的无限集

二:模拟 递归

计算函数值

数的计算

三:进制转换

进制(任意进制转十进制)

进制转换(任意进制转任意进制)

四:博弈论

Alice和Bob的爱恨情仇

五:前缀和 枚举

区间次方和

小郑的蓝桥平衡串

大石头的搬运工

最大数组和

五Plus:前缀和 桶 贪心 DP(动态规划) (记忆化)

 小郑的蓝桥平衡串

六:差分

区间更新

小明的彩灯

肖恩的投球游戏

 泡澡

七:二维 前缀和 差分

肖恩的投球游戏加强版

八:离散化

九:贪心

最小化战斗力差距

谈判

最大的卡牌价值

珠宝的最大交替和

小蓝的礼物

鸡哥的购物挑战

冒险者公会

明日方舟大作战

体育健将

十:双指针

回文判定

美丽的区间

挑选子串

聪明的小羊肖恩

神奇的数组

十一:二分

 二分查找数组元素(模板):二分查找数组元素

跳石头:跳石头

肖恩的苹果林:肖恩的苹果林

肖恩的乘法表:

可凑成的最大花束数

最大通过数

妮妮的月饼工厂:妮妮的月饼工厂

基德的神秘冒险:基德的神秘冒险

十二:滑动窗口 单调栈

四元组问题:四元组问题

十三:快速幂

快速幂:快速幂

十四:LCA(倍增)

最近公共祖先LCA查询:最近公共祖先LCA查询

十五:倍增

如今仍是遥远的理想之城1

数的变换

十六:位运算

位运算:位运算

区间或:区间或

异或森林:异或森林

最小的或运算:最小的或运算​编辑

简单的异或难题:简单的异或难题

出列:出列

小蓝学位运算:小蓝学位运算

位移:位移


一:模拟 暴力 枚举 构造 思维

模拟: 根据题目意思,把对应的代码按题意展示出来

暴力:不考察算法,正常按照逻辑计算

枚举:符合题意的每一个可能的值进行运算

构造:和模拟类似

思维:按照题意相同的意思,但解法可以不依赖本身题意,通过算法,更快的解决问题

特别数的和

特别数的和

//模拟 暴力 枚举
//特别数的和
//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

回文日期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序列修正

//模拟 思维
//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的爱恨情仇

//博弈论 
//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;
}

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值