#include <bits/stdc++.h>
#define ios \
ios::sync_with_stdio(false); \
cin.tie(nullptr); \
cout.tie(nullptr)
#define rep(i,a,n) for(int i=a;i<=n;i++)
#define per(i,a,n) for(int i=n;i>=a;i--)
#define lowbit(x) ((x) & - (x))
#define pb push_back
#define SZ(v) ((int)v.size())
#define PI acos(-1)
#define x first
#define y second
#define mem(a,b) memset((a),(b),sizeof(a))
using namespace std;
typedef long long ll;
const int INF=0x3f3f3f3f;
const ll LLINF=0x3f3f3f3f3f3f3f3fLL;
const double eps=1e-6;
const int MAX=2e5+10;
const ll mod=1e9+7;
/********************************* std-head *********************************/
int gcd(int a, int b){ //最大公约数
return b? gcd(b, a%b):a;
}
int lcm(int a, int b){ //最小公倍数
return a / gcd(a, b) * b;
}
bool isleap(int y) {//判断闰年
return (y % 4 == 0 && y % 100 != 0) || (y % 400 == 0);//闰年可以被4整除但不能被100整除,或能被400整除。
}
bool check(int year, int mouth, int day) {//判断日期是否合法
if (mouth > 12 || mouth == 0)return false;
if (day > 31)return false;
if (mouth == 2) {
if (isleap(year) && day > 29)
return false;
if (!isleap(year) && day > 28)
return false;
}
if (mouth == 4 || mouth == 6 || mouth == 9 || mouth == 11)
if (day > 30)return false;
return true;
}
struct sta {
bool m[n][n];
bool operator<(const sta &rhs) const {
return memcmp(m, rhs.m, sizeof(m)) < 0 ; //便于set去重时的排序
}
};
sta st;
bool dis[n][n];
void ff_(int x, int y) { //检验连通性(原理dfs递归)
dis[x][y] = true;
for (int i = 0; i < 4; ++i) {
int nx = x + dx[i], ny = y + dy[i];
if (nx < 0 || nx > n - 1 || ny < 0 || ny > n - 1) continue;
if (dis[nx][ny] || st.m[nx][ny] != st.m[x][y]) continue; //只填未重复,和颜色相同的块
ff_(nx, ny);
}
}
bool ff() {
memset(dis, 0, sizeof(dis));
int cnt = 0;
for (int i = 0; i < n; ++i) {
for (int j = 0; j < n; ++j)
if (!dis[i][j]) {
ff_(i, j);
++cnt;
}
}
return cnt == 1; //一块连通区域
}
sort(a.begin(),a.end(),greater<int>());//从大到小排序,输出 8 7 6 5 5 4 3 2
sort(s.begin(),s.end()); //字符串内部排序,得到全排列数列
do{
cout<<s<<endl;
}while(next_permutation(s.begin(),s.end()));
int bin_search(vector<int> &nums,int target)
{
int n = nums.size();
int left = 0;
int right = n - 1; // 定义target在左闭右闭的区间里,[left, right]
while (left <= right) { // 当left==right,区间[left, right]依然有效
int middle = left + ((right - left) / 2);// 防止溢出 等同于(left + right)/2
if (nums[middle] > target) {
right = middle - 1; // target 在左区间,所以[left, middle - 1]
} else if (nums[middle] < target) {
left = middle + 1; // target 在右区间,所以[middle + 1, right]
} else { // nums[middle] == target
return middle;
}
}
// 分别处理如下四种情况
// 目标值在数组所有元素之前 [0, -1]
// 目标值等于数组中某一个元素 return middle;
// 目标值插入数组中的位置 [left, right],return right + 1
// 目标值在数组所有元素之后的情况 [left, right], return right + 1
return right + 1;
} //若考虑实数二分,不用考虑整数的取整问题,所以可以直接左右都令为mid
const int maxn = 100001; //倍增法求区间最值
int n,m;
int a[maxn],dp_max[maxn][40]; //dp_min[maxn][40];
void st_init(){
for(int i=1;i<=n;i++) //初始化区间长度为1时的值
dp_max[i][0]=a[i]; //最大值。如果题目求区间最小值就改为dp_min
int p = (int)(log(double(n)) / log(2.0));
//int p=log2(n); //两者写法都行
for(int k=1;k<=p;k++) //倍增计算小区间。先算小区间,再算大区间,逐步递推
for(int s=1;s+(1<<k)<=n+1;s++)
dp_max[s][k]=max(dp_max[s][k-1], dp_max[s+(1<<(k-1))][k-1]);
//最大值。如果题目求区间最小值就改为dp_min、min
}
int st_query(int L,int R){
// int k = log2(R-L+1); //2种方法求k
int k = (int)(log(double(R-L+1)) / log(2.0));
return max(dp_max[L][k],dp_max[R-(1<<k)+1][k]);
//最大值。如果题目求区间最小值就改为dp_min、min
}
int main(){
scanf("%d%d",&n,&m); //n个元素中查询m个区间
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
st_init();
for(int i=1;i<=m;i++){
int L,R;
scanf("%d%d",&L,&R);
printf("%d\n",st_query(L,R));
}
return 0;
}
for(int i = 1; i <= n ; i ++)
sum[i] = sum[i - 1] + a[i];//计算前缀和时数组下标一定要从1开始,方便计算
//常用于需要多次查找某一区间和的问题
void insert(int l,int r,int c) //一维差分处理,相当于给l,r区间每个a[]元素值加c
{
b[l]+=c;
b[r+1]-=c;
}insert(i,i,a[i]);
void insert(int x1,int y1,int x2,int y2,int c) //二维差分处理
{
b[x1][y1]+=c;
b[x1][y2+1]-=c;
b[x2+1][y1]-=c;
b[x2+1][y2+1]+=c;
}insert(i,j,i,j,a[i][j]);
void Merge(ll l, ll mid, ll r){ //归并排序处理逆序对问题
ll i=l, j = mid+1, t=0;
while(i <= mid && j <= r){
if(a[i] > a[j]){
b[t++] = a[j++];
cnt += mid-i+1; //记录逆序对数量
}
else b[t++]=a[i++];
}
//一个子序列中的数都处理完了,另一个还没有,把剩下的直接复制过来:
while(i <= mid) b[t++]=a[i++];
while(j <= r) b[t++]=a[j++];
for(i=0; i<t; i++) a[l+i] = b[i]; //把排好序的b[]复制回a[]
}
void Mergesort(ll l, ll r){
if(l<r){
ll mid = (l+r)/2; //平分成两个子序列
Mergesort(l, mid);
Mergesort(mid+1, r);
Merge(l, mid, r); //合并
}
}
int a[20]={1,2,3,4,5,6,7,8,9,10,11,12,13}; //自写全排列,从小到大生成新的数组b[]
bool vis[20]; //记录第i个数是否用过
int b[20]; //生成的一个全排列
void dfs(int s,int t){
if(s == t) { //递归结束,产生一个全排列
for(int i = 0; i < t; ++i) //输出一个排列
cout << b[i] << " ";
cout<<endl;
return;
}
for(int i=0;i<t;i++)
if(!vis[i]){
vis[i]=true;
b[s]=a[i];
dfs(s+1,t);
vis[i]=false;
}
}
int d[4][2] = {{0,1}, {0,-1}, {1,0}, {-1,0}};
ans; //答案,用全局变量表示
void dfs(层数,其他参数){
if (出局判断){ //到达最底层,或者满足条件退出
更新答案; //答案一般用全局变量表示
return; //返回到上一层
}
(剪枝) //在进一步DFS之前剪枝
for (枚举下一层可能的情况) //对每一个情况继续DFS
if (vis[i] == 0) { //如果状态i没有用过,就可以进入下一层
vis[i] = 1; //标记状态i,表示已经用过,在更底层不能再使用
dfs(层数+1,其他参数); //下一层
vis[i] = 0; //恢复状态,回溯时,不影响上一层对这个状态的使用
}
return; //返回到上一层
}
const int maxn = 8e5+5; //并查集模板,合并集合
int s[maxn];
void init_set(){ //初始化
for(int i = 1; i <= maxn; i++)
s[i] = i;
}
int find_set(int x){
if(x != s[x])
s[x] = find_set(s[x]); //路径压缩
return s[x];
}
void merge_set(int x, int y){ //合并
x = find_set(x);
y = find_set(y);
if(x != y) s[x] = s[y];
}
int tree[N]={0}; //树状数组处理动态区间和
void update(int x, int d) { //修改元素a[x], a[x] = a[x] + d
while(x <= N) {
tree[x] += d;
x += lowbit(x);
}
}
int sum(int x) { //返回前缀和sum = a[1] + a[2] +... + a[x]
int ans = 0;
while(x > 0){
ans += tree[x];
x -= lowbit(x);
}
return ans;
}
long long ans=0;
for(int i=n;i>0;--i){ //倒序处理,计算序列rank[]的逆序对数量
update(Rank[i],1);
ans += sum(Rank[i]-1);
}
//线段树板子,可求区间最值,区间和
const int MAXN = 1e5 + 10;
ll a[MAXN]; //记录数列的元素,从a[1]开始
ll tree[MAXN<<2];//tree[i]:第i个结点的值,表示一个线段区间的值,例如最值、区间和
ll tag[MAXN<<2]; //tag[i]:第i个结点的lazy-tag,统一记录这个区间的修改
ll ls(ll x){ return x<<1; } //定位左儿子:x*2
ll rs(ll x){ return x<<1|1;} //定位右儿子:x*2 + 1
void push_up(ll p){ //从下往上传递区间值
tree[p] = tree[ls(p)] + tree[rs(p)];
//本题是区间和。如果求最小值,改为:tree[p] = min(tree[ls(p)], tree[rs(p)]);
}
void build(ll p,ll pl,ll pr){ //建树。p是结点编号,它指向区间[pl, pr]
tag[p] = 0; //lazy-tag标记
if(pl==pr){tree[p]=a[pl]; return;} //最底层的叶子,赋值
ll mid = (pl+pr) >> 1; //分治:折半
build(ls(p),pl,mid); //左儿子
build(rs(p),mid+1,pr); //右儿子
push_up(p); //从下往上传递区间值
}
void addtag(ll p,ll pl,ll pr,ll d){ //给结点p打tag标记,并更新tree
tag[p] += d; //打上tag标记
tree[p] += d*(pr-pl+1); //计算新的tree
}
void push_down(ll p,ll pl,ll pr){ //不能覆盖时,把tag传给子树
if(tag[p]){ //有tag标记,这是以前做区间修改时留下的
ll mid = (pl+pr)>>1;
addtag(ls(p),pl,mid,tag[p]); //把tag标记传给左子树
addtag(rs(p),mid+1,pr,tag[p]); //把tag标记传给右子树
tag[p]=0; //p自己的tag被传走了,归0
}
}
void update(ll L,ll R,ll p,ll pl,ll pr,ll d){ //区间修改:把[L, R]内每个元素加上d
if(L<=pl && pr<=R){ //完全覆盖,直接返回这个结点,它的子树不用再深入了
addtag(p, pl, pr,d); //给结点p打tag标记,下一次区间修改到p这个结点时会用到
return;
}
push_down(p,pl,pr); //如果不能覆盖,把tag传给子树
ll mid=(pl+pr)>>1;
if(L<=mid) update(L,R,ls(p),pl,mid,d); //递归左子树
if(R>mid) update(L,R,rs(p),mid+1,pr,d); //递归右子树
push_up(p); //更新
}
ll query(ll L,ll R,ll p,ll pl,ll pr){
//查询区间[L,R];p是当前结点(线段)的编号,[pl,pr]是结点p表示的线段区间
if(pl>=L && R >= pr) return tree[p]; //完全覆盖,直接返回
push_down(p,pl,pr); //不能覆盖,递归子树
ll res=0;
ll mid = (pl+pr)>>1;
if(L<=mid) res+=query(L,R,ls(p),pl,mid); //左子结点有重叠
if(R>mid) res+=query(L,R,rs(p),mid+1,pr); //右子结点有重叠
return res;
}
ll n, m; scanf("%lld%lld",&n,&m); //n个点,m个操作
for(ll i=1;i<=n;i++) scanf("%lld",&a[i]);
build(1,1,n); //建树
while(m--){
ll q,L,R,d;
scanf("%lld",&q);
if (q==1){ //区间修改:把[L,R]的每个元素加上d
scanf("%lld%lld%lld",&L,&R,&d);
update(L,R,1,1,n,d);
}
else { //区间询问:[L,R]的区间和
scanf("%lld%lld",&L,&R);
printf("%lld\n",query(L,R,1,1,n));
}
}
//计算几何
struct Point{ //点的表示
double x,y;
Point(){}
Point(double x,double y):x(x),y(y){}
Point operator - (Point B){return Point(x-B.x,y-B.y);} //定义减法
};
double Dist(Point A,Point B){ //两点间距离公式
return sqrt((A.x-B.x)*(A.x-B.x) + (A.y-B.y)*(A.y-B.y));
}
typedef Point Vector; //表示向量
double Dot(Vector A,Vector B){return A.x*B.x + A.y*B.y;} //求向量A与向量B的点积
double Len(Vector A){return sqrt(Dot(A,A));} // 求向量A的长度
double Angle(Vector A,Vector B){ // 求向量A和向量B的夹角大小
return acos(Dot(A,B)/Len(A)/Len(B));
}
double Cross(Vector A,Vector B){return A.x*B.y – A.y*B.x;} // 求向量A与向量B的叉积
double Area2(Point A,Point B,Point C){
return Cross(B-A, C-A); //计算向量AB与向量AC构成的平行四边形的面积
}
Vector Rotate(Vector A, double rad){ //使向量A逆时针旋转rad度,得到的新向量
return Vector(A.x*cos(rad)-A.y*sin(rad), A.x*sin(rad)+A.y*cos(rad));
}
bool Parallel(Vector A, Vector B){ // 用叉积检查两个向量是否平行或重合
return sgn(Cross(A,B)) == 0;
}
// 点和线的关系
struct Line{
Point p1,p2; //线上的两个点
Line(){}
Line(Point p1,Point p2):p1(p1),p2(p2){}
//根据一个点和倾斜角 angle 确定直线,0<=angle<pi
Line(Point p,double angle){
p1 = p;
if(sgn(angle – pi/2) == 0){p2 = (p1 + Point(0,1));}
else{p2 = (p1 + Point(1,tan(angle)));}
}
//ax+by+c=0
Line(double a,double b,double c){
if(sgn(a) == 0){
p1 = Point(0,-c/b);
p2 = Point(1,-c/b);
}
else if(sgn(b) == 0){
p1 = Point(-c/a,0);
p2 = Point(-c/a,1);
}
else{
p1 = Point(0,-c/b);
p2 = Point(1,(-c-a)/b);
}
}
};
int Point_line_relation(Point p, Line v){ //判断点p与线v的位置关系
int c = sgn(Cross(p-v.p1,v.p2-v.p1));
if(c < 0) return 1; //1:p在v的左边
if(c > 0) return 2; //2:p在v的右边
return 0; //0:p在v上
}
bool Point_on_seg(Point p, Line v){ //点和线段:0 点不在线段v上;1 点在线段v上
return sgn(Cross(p-v.p1, v.p2-v.p1)) == 0 && sgn(Dot(p – v.p1,p- v.p2)) <= 0;
}
double Dis_point_line(Point p, Line v){ //点到直线的距离公式
return fabs(Cross(p-v.p1,v.p2-v.p1))/Distance(v.p1,v.p2);
}
//点在直线上的投影
Point Point_line_proj(Point p, Line v){
double k=Dot(v.p2-v.p1,p-v.p1)/Len2(v.p2-v.p1);
return v.p1+(v.p2-v.p1)*k;
}
//获取点关于直线的对称点
Point Point_line_symmetry(Point p, Line v){
Point q = Point_line_proj(p,v);
return Point(2*q.x-p.x,2*q.y-p.y);
}
int Line_relation(Line v1, Line v2){ //判断两条直线的位置关系
if(sgn(Cross(v1.p2-v1.p1,v2.p2-v2.p1)) == 0){
if(Point_line_relation(v1.p1,v2)==0) return 1; //1 重合
else return 0; //0 平行
}
return 2; //2 相交
}
//判断两条直线是否相交
bool Cross_segment(Point a,Point b,Point c,Point d){//Line1:ab, Line2:cd
double c1=Cross(b-a,c-a),c2=Cross(b-a,d-a);
double d1=Cross(d-c,a-c),d2=Cross(d-c,b-c);
return sgn(c1)*sgn(c2)<=0 && sgn(d1)*sgn(d2)<=0; //1相交;0不相交
}
ll fastPow(ll a, ll n, ll mod){ //快速幂取模
ll ans = 1;
a %= mod; //重要,防止下面的ans*a越界
while(n) {
if(n & 1) ans = (ans*a) % mod; //取模
a = (a*a) % mod; //取模
n >>= 1;
}
return ans;
}
//矩阵快速幂
struct matrix{ int m[N][N]; }; //定义矩阵,常数N是矩阵的行数和列数
matrix operator * (const matrix& a, const matrix& b){
//重载*为矩阵乘法。注意const
matrix c;
memset(c.m, 0, sizeof(c.m)); //清零
for(int i=0; i<N; i++)
for(int j=0; j<N; j++)
for(int k = 0; k<N; k++)
//c.m[i][j] += a.m[i][k] * b.m[k][j]; //不取模
c.m[i][j] = (c.m[i][j] + a.m[i][k] * b.m[k][j]) % mod;//取模
return c;
}
matrix pow_matrix(matrix a, int n){ //矩阵快速幂,代码和普通快速幂几乎一样
matrix ans;
memset(ans.m,0,sizeof(ans.m));
for(int i=0;i<N;i++)
ans.m[i][i] = 1; //初始化为单位矩阵,类似普通快速幂的ans=1
while(n) {
if(n&1) ans = ans * a; //不能简写为ans *= a,这里的*重载了
a = a * a;
n>>=1;
}
return ans;
}
bool is_prime(long long n){ //试除法判断素数
if(n <= 1) return false; //1不是素数
for(long long i=2; i*i <= n; i++)
//或者这样写:i <= sqrt(n),总计算量更少
if(n % i == 0) return false; //能整除,不是素数
return true; //读者思考,n=2时返回true吗?
}
//埃式筛:
const int MAXN = 1e7; //定义空间大小,1e7约10M
int prime[MAXN+1]; //存放素数,它记录visit[i] = false的项
bool visit[MAXN+1]; //true表示被筛掉,不是素数
int E_sieve(int n) {
for(int i = 0; i <= n; i++) visit[i]= false;
for(int i = 2; i*i <= n; i++) //改为i<=sqrt(n),循环计算更快
if(!visit[i])
for(int j=i*i; j<=n; j+=i)
visit[j] = true; //j不是素数
//下面记录素数
int k=0; //统计素数个数
for(int i = 2; i <= n; i++)
if(!visit[i])
prime[k++] = i; //存储素数
return k;
}
//区间素数(两重埃式筛)
ll seg_sieve(ll a,ll b) {
for(ll i=0;i*i<=b;++i) vis[i]=true;
for(ll i=0;i<=b-a;++i) seg_prime[i]=true;
for(ll i=2;i*i<=b;++i)
if(vis[i]) { //i是素数
for(ll j=i*i;j*j<=b;j+=i)//筛选[2,sqrt(b)]内的素数
vis[j] = false;
for(ll j=max(2LL,(a+i-1)/i)*i;j<=b;j+=i)//筛选[a,b]内的素数
seg_prime[j-a] = false;
}
ll num=0;
for(ll i=0;i<=b-a;++i) //统计[a,b]内素数个数
if(seg_prime[i])
prime[num++]=i+a; //存[a,b]内的素数
return num;
}
//试除法求一个数的质因子
int p[20]; //p[]记录因子,p[1]是最小因子。一个int数的质因子最多有10几个
int c[40]; //c[i]记录第i个因子的个数。一个因子的个数最多有30几个
int factor(int n){
int m = 0;
for(int i = 2; i <= sqrt(n); i++)
if(n%i == 0){
p[++m] = i, c[m] = 0;
while(n%i == 0) //把n中重复的因子去掉
n/=i, c[m]++;
}
if(n>1) //n没有被除尽,是素数
p[++m] = n, c[m] = 1;
return m; //共m个因子
}
//计算组合数
ll C(int n,int m) { //计算组合数n中取m
if(n==m||m==0) return 1;
else if(n<m) return 0;
else{
ll res=1; //防止溢出,边乘边除,先乘值小的
n=n-m+1;
for(int i=1;i<=m;i++){
res*=n++;
res/=i;
}
return res;
}
}
int dfs(int n,int m){ //dfs递归计算
if(!m) return c[n][m]=true;
if(m==1) return c[n][m]=n;
if(c[n][m]) return c[n][m];
if(n-m<m )m=n-m;
return c[n][m]=(dfs(n-1,m)+dfs(n-1,m-1))%mod;
}
//邻接表
struct edge{
int from,to,w;
edge(int a,int b,int c){form=a;to=b;w=c;}
};
vector<edge> e[NUM]; //e[i]:存第i个结点连接的所有边
for(int i=1;i<=n;i++)
e[i].clear();
e[a].push_back(edge(a,b,c)); //把边(a,b)存到结点a的邻接表中
for(int i=0;i<e[u].size();i++){ //检索结点u的所有邻居
...
}
//Floyd算法计算(计算所有点对之间的最短路,可重复走) n<300;
for(int k=1; k<=n; k++) //floyd的三重循环
for(int i=1; i<=n; i++)
for(int j=1; j<=n; j++) // k循环在i、j循环外面
dp[i][j] = min(dp[i][j], dp[i][k] + dp[k][j]); //比较:不经过k、经过k
//C++字符串函数
#include<bits/stdc++.h>
using namespace std;
int main(){
string str ="123456789abcdefghiaklmn";
for(int i=0;i<10;i++) //把str看成一个字符串数组
cout<<str[i]<<" ";
cout << endl;
//find函数
cout<<"123的位置: "<<str.find("123")<<endl;
//输出:123的位置: 0
cout<<"34在str[2]到str[n-1]中的位置: "<<str.find("34",2)<<endl;
//输出:34在str[2]到str[n-1]中的位置: 2
cout<<"ab在str[0]到str[12]中的位置: "<<str.rfind("ab",12)<<endl;
//输出:ab在str[0]到str[12]中的位置: 9
//substr()函数
cout<<"str[3]及以后的子串:"<<str.substr(3)<<endl;
//输出:str[3]及以后的子串:456789abcdefghijklmn
//若小于限制长度则报错
cout<<"从str[2]开始的4个字符:"<<str.substr(2,4)<<endl;
//输出:从str[2]开始的4个字符:3456
//find()函数
str.replace(str.find("a"), 5, "@#");
cout<<str<<endl;
//输出:123456789@#fghiaklmn
//insert()函数
str.insert(2, "***");
cout<<"从2号位置插入: "<<str<<endl;
//输出:12***3456789@#fghiaklmn
//添加字符串:append()函数
str.append("$$$");
cout<<"在字符串str后面添加字符串:"<<str<<endl;
//输出: 12***3456789@#fghiaklmn$$$
//字符串长度
cout<<str.size()<<endl;
cout<<str.length()<<endl;
//交换字符串:swap()函数
string str1="aaa",str2="bbb";
swap(str1, str2);
cout<<str1<<" "<<str2<<endl;
//字符串比较函数:compare(),相等输出0,不等输出1
cout<<str1.compare(str2)<<endl;
if(str1==str2) cout <<"=="; //直接比较也行
if(str1!=str2) cout <<"!=";
return 0;
}