#define ls p<<1
#define rs p<<1|1
#define lson l,m,ls
#define rson m+1,r,rs
int sum[maxn<<2];//空间要开四倍
void push_up(int p){
sum[p]=sum[ls]+sum[rs];
}
void build(int l,int r,int p){
if(l==r){
scanf("%d",&sum[p]);
return ;
}
int m=(l+r)>>1;
build(lson);
build(rson);
push_up(p);//向上更新
}
//调用 build(1,n,1);
void update(int x,int num,int l,int r,int p){
if(l==r){
sum[p]+=num;
return;
}
int m=(l+r)>>1;
if(x<=m)update(x,num,lson);
else update(x,num,rson);
push_up(p);
}
//将第五个元素的值加num
//调用 update(5,num,1,n,1);
int query(int L,int R,int l,int r,int p){
if(L<=l&&r<=R){
return sum[p];
}
int m=(l+r)>>1;
int ans=0;
if(L<=m)ans+=query(L,R,lson);
if(R>m)ans+=query(L,R,rson);
return ans;
}
//查询1-3的值的和
//调用 query(1,3,1,n,1);
求最小逆序对
题目链接点这里题意:不断把第一个数放在末尾,求在此过程中序列的最小逆序对数
解析:由于数值间唯一且不重复,且n不大,所以可以先以原序列建一棵线段树,维护不同时段1-n数的个数,在进行一次统计之后,进行一次更新,因为求逆序对数也即是统计当前时刻在线段树中比其大的数的个数。
然后就是不断把第一个数放到末尾,设当前数为x,由于在未放之前,比其大的数有n-x个,比其小的有x-1个,所以放到末尾之后,逆序对数相对于x未放之前增加了n-x-(x-1)个,所以扫一遍取最小就行了。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
typedef long long ll;
typedef pair<int,int> P;
const int inf=0x3f3f3f3f;
const int maxn=5e3+10;
#define lson l,m,p<<1
#define rson m+1,r,p<<1|1
int n;
int arr[maxn];
int sum[maxn<<2];
void push_up(int p){
sum[p]=sum[p<<1]+sum[p<<1|1];
}
void build(int l,int r,int p){
sum[p]=0;
if(l==r){
return ;
}
int m=(l+r)>>1;
build(lson);
build(rson);
}
int query(int L,int R,int l,int r,int p){
if(L<=l&&r<=R){
return sum[p];
}
int m=(l+r)>>1;
if(R<=m)return query(L,R,lson);
if(L>m)return query(L,R,rson);
return query(L,m,lson)+query(m+1,R,rson);
}
void update(int num,int l,int r,int p){
if(l==r){
sum[p]++;
return;
}
int m=(l+r)>>1;
if(num<=m)update(num,lson);
else update(num,rson);
push_up(p);
}
int main() {
while(~scanf("%d",&n)){
int sum=0;
build(1,n,1);
for(int i=1;i<=n;i++){
scanf("%d",&arr[i]);
++arr[i];//将0-n-1变成1-n
sum+=query(arr[i],n,1,n,1);
update(arr[i],1,n,1);
}
int ans=sum;
for(int i=1;i<=n;i++){
sum+=n-arr[i]-(arr[i]-1);
ans=min(ans,sum);
}
cout<<ans<<'\n';
}
return 0;
}
#define ls p<<1
#define rs p<<1|1
int sum[maxn<<2],lazy[maxn<<2];
void push_down(int p,int l,int r){//区间每个数都加上一个数
if(lazy[p]==0)return;
int m=(l+r)>>1;
lazy[ls]+=lazy[p];
lazy[rs]+=lazy[p];
sum[ls]+=lazy[p]*(m-l+1);
sum[rs]+=lazy[p]*(r-m);
lazy[p]=0;
}
//相应的query和update也有部分修改
题目链接点这里
区间更新的一道裸体,就不过多解释了
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <stack>
using namespace std;
typedef long long ll;
typedef pair<int,int> P;
const int inf=0x3f3f3f3f;
const int maxn=1e5+10;
#define ls p<<1
#define rs p<<1|1
#define lson l,m,p<<1
#define rson m+1,r,p<<1|1
int n,a,b;
int sum[maxn<<2],lazy[maxn<<2];
void push_up(int p){
sum[p]=sum[ls]+sum[rs];
}
void push_down(int p,int l,int r){
if(lazy[p]==0)return;
int m=(l+r)>>1;
lazy[ls]+=lazy[p];
lazy[rs]+=lazy[p];
sum[ls]+=lazy[p]*(m-l+1);
sum[rs]+=lazy[p]*(r-m);
lazy[p]=0;
}
void build(int l,int r,int p){
sum[p]=0;
lazy[p]=0;
if(l==r){
return ;
}
int m=(l+r)>>1;
build(lson);
build(rson);
}
int query(int L,int R,int l,int r,int p){
if(L<=l&&r<=R){
return sum[p];
}
push_down(p,l,r);
int m=(l+r)>>1;
if(R<=m)return query(L,R,lson);
if(L>m)return query(L,R,rson);
return query(L,m,lson)+query(m+1,R,rson);
}
void update(int L,int R,int l,int r,int p){
if(L<=l&&r<=R){
lazy[p]++;
sum[p]+=(r-l+1);
return;
}
push_down(p,l,r);//在继续修改前,先检查是否要下传标记
int m=(l+r)>>1;
if(R<=m)update(L,R,lson);
else if(L>m)update(L,R,rson);
else update(L,m,lson),update(m+1,R,rson);
push_up(p);//回溯更新每个节点的sum,因为每个节点的值改变了
}
int main() {
while(~scanf("%d",&n)&&n){
build(1,n,1);
for(int i=1;i<=n;i++){
scanf("%d%d",&a,&b);
update(a,b,1,n,1);
}
for(int i=1;i<=n;i++){
printf("%d%c",query(i,i,1,n,1),i==n?'\n':' ');
}
}
return 0;
}
链接点这里
题意:给你一个序列s[],s[i]表示它之前所有小于它的数的和,让你求p[]数组,元素大小在[1,n]范围且唯一。
解析:我们不难发现1在s[]中最后一个0所在位置处,去掉1并将其后所有元素减一后,2就在新序列最后一个0所在位置,以此类推,就能求出p数组。由于涉及区间更新,和查询,所以用线段树可以高效解决。
复杂度:O(nlogn)
#include <iostream>
#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;
typedef long long ll;
const int maxn=2e5+10;
const ll inf=1e15;
#define faster ios::sync_with_stdio(0),cin.tie(0)
#define lson l,m,p<<1
#define rson m+1,r,p<<1|1
#define ls p<<1
#define rs p<<1|1
int n;
ll s[maxn];
ll mm[maxn<<2],lazy[maxn<<2];
int ans[maxn];
void push_up(int p){
mm[p]=min(mm[ls],mm[rs]);
}
void push_down(int p){
if(lazy[p]==0)return;
lazy[ls]+=lazy[p];
lazy[rs]+=lazy[p];
mm[ls]+=lazy[p];//如果维护的是和则需要乘(m-l+1)
mm[rs]+=lazy[p];//上同需要乘(r-m)
lazy[p]=0;
}
void build(int l,int r,int p){
if(l==r){
mm[p]=s[l];
return ;
}
int m=(l+r)>>1;
build(lson);
build(rson);
push_up(p);
}
void update(int pos,ll x,int l,int r,int p){
if(l==r){
mm[p]=x;lazy[p]=0;
return ;
}
push_down(p);
int m=(l+r)>>1;
if(pos<=m)update(pos,x,lson);
else update(pos,x,rson);
push_up(p);
}
void update(int L,int R,ll x,int l,int r,int p){
if(L<=l&&r<=R){
lazy[p]+=x;
mm[p]+=x;
return ;
}
push_down(p);
int m=(l+r)>>1;
if(R<=m)update(L,R,x,lson);
else if(L>m)update(L,R,x,rson);
else update(L,m,x,lson),update(m+1,R,x,rson);
push_up(p);
}
int query(int l,int r,int p){//查询最后一个0所在位置
if(l==r){
return l;
}
push_down(p);
int m=(l+r)>>1;
if(mm[rs]==0)return query(rson);
return query(lson);
}
int main()
{
faster;
cin>>n;
for(int i=1;i<=n;i++){
cin>>s[i];
}
build(1,n,1);
for(int i=1;i<=n;i++){
int pos=query(1,n,1);
ans[pos]=i;
update(pos,inf,1,n,1);
if(pos<n)update(pos+1,n,(ll)-i,1,n,1);
}
for(int i=1;i<=n;i++)printf("%d%c",ans[i],i==n?'\n':' ');
return 0;
}
(1)最大值,最小值
(2)数字之和
(3)gcd
题目链接点这里
题意:简而言之就是一个序列初始都为1,现在每次可以使一个点变为0,每次也可以把之前变为0的点变回1,现在要求你在线查询其中的某一点(包括他自己)的1的最大连续长度。
解析:用线段树维护,对于每个点,我们需要维护三个信息:
1.从该区间左端点出发连续的最大长度;(设其为lx)
2.从该区间右端点出发连续的最大长度;(设其为rx)
3.该区间的最大连续长度; (设其为mx)
单点更新时,就是把对应的叶子节点变为1或0,然后pushup,pushup时要注意怎样合并这三个信息。
一.首先父节点的lx最小也应该是左儿子的lx,rx同理;
然后其mx一定是:左儿子的mx,右儿子的mx,左儿子的rx+右儿子的lx 三者的最大值。(应该不难理解)
现在还有一个问题就是如果他左儿子的lx是满的(即lx值为左儿子维护区间大小),那么它的lx就需要加上右儿子的lx;他的rx同理。(此处重点理解一下,最好在本子上画画,就很清晰了)
二.然后就是查询了,我们要查包含某个点的最大连续1的长度,也就是查询包括这个点的所有区间中1最多的那个。
所以从根节点出发查询即可,注意剪枝。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <stack>
using namespace std;
typedef long long ll;
typedef pair<int,int> P;
const int inf=0x3f3f3f3f;
const int maxn=5e4+10;
#define ls p<<1
#define rs p<<1|1
#define lson l,m,ls
#define rson m+1,r,rs
#define faster ios::sync_with_stdio(0),cin.tie(0)
int m,n;
struct tree{
int l,r,lx,rx,mx;
}sum[maxn<<2];
void pushup(int p){
sum[p].lx=sum[ls].lx;
sum[p].rx=sum[rs].rx;
sum[p].mx=max(sum[ls].mx,sum[rs].mx);
sum[p].mx=max(sum[p].mx,sum[ls].rx+sum[rs].lx);
if(sum[ls].lx==sum[ls].r-sum[ls].l+1){
sum[p].lx+=sum[rs].lx;
}
if(sum[rs].rx==sum[rs].r-sum[rs].l+1){
sum[p].rx+=sum[ls].rx;
}
}
void build(int l,int r,int p){
sum[p].l=l;sum[p].r=r;sum[p].lx=sum[p].rx=sum[p].mx=r-l+1;
if(l==r)return;
int m=(l+r)>>1;
build(lson);
build(rson);
}
void update(int x,int num,int p){
if(sum[p].l==sum[p].r){
sum[p].lx=sum[p].rx=sum[p].mx=num;
return ;
}
int m=(sum[p].l+sum[p].r)>>1;
if(x<=m)update(x,num,ls);
if(x>m)update(x,num,rs);
pushup(p);
}
int query(int x,int p){
//如果区间是满的或是0就可以返回了
if(sum[p].l==sum[p].r||sum[p].mx==0||sum[p].mx==sum[p].r-sum[p].l+1){
return sum[p].mx;
}
int m=(sum[p].l+sum[p].r)>>1;
if(x<=m){
//要求的区间会不会涉及到右儿子
if(x>=sum[ls].r-sum[ls].rx+1){
return query(x,ls)+query(m+1,rs);
}
else return query(x,ls);
}
else {
if(x<=sum[rs].l+sum[rs].lx-1){
return query(x,rs)+query(m,ls);
}
else return query(x,rs);
}
}
stack<int> s;
int main()
{
while(~scanf("%d%d",&n,&m)){
while(!s.empty())s.pop();
build(1,n,1);
for(int i=1;i<=m;i++){
char t;int x;
cin>>t;
if(t=='D'){
scanf("%d",&x);
update(x,0,1);
s.push(x);
}
if(t=='Q'){
scanf("%d",&x);
cout<<query(x,1)<<'\n';
}
if(t=='R'){
if(!s.empty()){
update(s.top(),1,1);
s.pop();
}
}
}
}
return 0;
}