文章目录
1 .敌兵布阵
传送门
题目大意:有四种操作,Add表示第i个营地增加j个人
Sub表示第i个营地减少j个人
Query表示询问i到j的和
End表示结束
贴代码
线段树代码
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<queue>
#include<stack>
#include<deque>
#include<map>
#include<set>
#include<cstdio>
#include<vector>
#define ll long long
#define mem(a,b) memset(a,b,sizeof(a))
const ll INF=0x3f3f3f3f;
const ll Max=50000+5;
using namespace std;
int i,j,k,t,n,m;
int num[Max*4];
/*queue<ll> q;
stack<ll> s;
//升序队列
priority_queue <int,vector<int>,greater<int> > q;
//降序队列
priority_queue <int,vector<int>,less<int> >q;*/
void change_segment(int k,int l,int r,int x);
void pushdown(int k);
struct node
{
int l/*区间左边界*/,r/*区间右边界*/,sum/*区间和*/,maxx/*区间最大值*/,minn/*区间最小值*/,lazy/*懒惰标记*/;
node()
{
l=r=sum=lazy=maxx=minn=0;
}
}a[Max*4];//N为总节点数
void update(int k)//更新节点k的信息
{ pushdown(k);
a[k].sum=a[k*2].sum+a[2*k+1].sum;
//很显然,一段区间的元素和等于它的子区间的元素和
//如果有懒惰标记的话要相应地改变(记得加上懒惰标记的值!!!)
}
void build(int k,int l,int r)
{
a[k].l=l;a[k].r=r;
if(l==r)//递归到了叶节点
{
a[k].sum+=num[l];
return ;
}
int mid=(l+r)/2;//计算左右子节点的分界
build(k*2,l,mid);//递归到左子节点
build(2*k+1,mid+1,r);//递归到右子节点
update(k);//用左右子节点更新父节点
}
//单点修改
void change_point(int k/*当前节点编号*/,int x/*要修改的编号*/,int y/*要修改为的数*/)
{
if(a[k].l==a[k].r) {a[k].sum+=y;return ;}
int mid=(a[k].l+a[k].r)/2;
if(x<=mid) change_point(k*2,x,y);//递归到左子节点
else change_point(2*k+1,x,y);//递归到右子节点
update(k);//更新k节点的数据
}
//区间修改
//相对标记指的是可以共存的标记,且打标记的顺序与答案无关,即标记可以叠加。 比如说给一段区间中的所有数字都 + a +a+a ,我们就可以把标记叠加一下,比如上一次打了一个 + 1 +1+1 的标记,这一次要给这一段区间 + 5 +5+5 ,那么就把 + 1 +1+1 的标记变成 + 6 +6+6 。
void change_segment(int k,int l,int r,int x)
//将[l,r]区间进行修改,x用于修改的
{
if(a[k].l==l&&a[k].r==r)//如果找到全部元素的区间
{
a[k].sum+=(r-l+1)*x;
//更新区间的数据
a[k].lazy+=x;return ;
//懒惰标记叠加
}
int mid=(a[k].l+a[k].r)/2;//区间完全在左子节点的区间内
if(r<=mid) change_segment(k*2,l,r,x);
else if(l>mid) change_segment(k*2+1,l,r,x);//区间完全在右子节点的区间内
else change_segment(k*2,l,mid,x),change_segment(k*2+1,mid,r,x);
update(k);
//更新数据
}
//绝对标记是指不可以共存的标记,每一次都要先把标记下传,再给当前节点打上新的标记。这些标记不能改变次序,否则会出错。 比如说给一段区间的数字重新赋值,或是给一段区间进行多种操作
//请注意:某些题目的懒惰标记属于 绝对标记 (如维护区间 平方和 ),一定要先 下传标记 ,再向下递归。
void pushdown(int k)//将节点k的懒惰标记下传
{
if(a[k].l==a[k].r){a[k].lazy=0;return ;}
//如果该节点是叶,则不需要下传,直接返回就可
a[k*2].sum+=(a[k*2].r-a[k*2].l+1)*a[k].lazy;
a[k*2+1].sum+=(a[k*2+1].r-a[k*2+1].l+1)*a[k].lazy;
//给节点k的子节点重新赋值
a[k*2].lazy+=a[k].lazy;
a[k*2+1].lazy+=a[k].lazy;
//下传节点k的标记
a[k].lazy=0;//清空节点k的标记
}
//记得在查询之前下传标记!!!
int query(int k,int l,int r)
//当前到达编号为k的节点,查寻[l.r]的和
{
if(a[k].lazy) pushdown(k);
//如果当前节点被打上了懒惰标记,那么就把这个标记下传,这一句其实也可以放在下一语句的后面
if(a[k].l==l&&a[k].r==r) return a[k].sum;
//如果当前区间就是询问区间,完全重合,那么显然可以直接返回
int mid=(a[k].l+a[k].r)/2;
if(r<=mid) return query(k*2,l,r);
//如果询问区间包含在左子区间中
if(l>mid) return query(k*2+1,l,r);
//如果询问区间包含在右子区间中
return query(k*2,l,mid)+query(k*2+1,mid+1,r);
//如果询问区间跨越两个子区间
}
int main()
{
scanf("%d",&t);
int y=0;
while(t--)
{
scanf("%d",&n);
for(i=1;i<=n;i++)
{
scanf("%d",&num[i]);
getchar();
}
build(1,1,n);
char b[12]={0};
y++;
printf("Case %d:\n",y);
while(scanf("%s",&b)&&strcmp(b,"End")!=0)
{
if(strcmp(b,"Query")==0)
{ int L,R;
scanf("%d%d",&L,&R);
int w=query(1,L,R);
printf("%d\n",w);
}
else if(strcmp(b,"Add")==0)
{
int add,q;
scanf("%d%d",&q,&add);
change_point(1,q,add);
}
else if(strcmp(b,"Sub")==0)
{
int add,q;
scanf("%d%d",&q,&add);
add=-add;
change_point(1,q,add);
}
getchar();
}mem(b,0);
mem(a,0);
}
return 0;
}
树状数组代码
在这里插入代码片#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<queue>
#include<stack>
#include<deque>
#include<map>
#include<bitset>
#include<set>
#include<cstdio>
#include<vector>
#define ll long long
#define PI 3.1415926
#define mem(a,b) memset(a,b,sizeof(a))
#define REPF( i , a , b ) for ( int i = a ; i <= b ; ++ i )
#define REP( i , n ) for ( int i = 0 ; i < n ; ++ i )
const ll INF=0x3f3f3f3f;
const ll Max=5*1e4+5;
using namespace std;
int t,n,m;
int c[Max];
/*queue<ll> q;
stack<ll> s;
//升序队列
priority_queue <int,vector<int>,greater<int> > q;
//降序队列
priority_queue <int,vector<int>,less<int> >q;*/
int lowbit(int x)
{
return x&(-x);
}
void update(int x,int d)
{
for(int i=x;i<=n;i+=lowbit(i))
c[i]+=d;
}
int getsum(int x)
{
int ans=0;
for(int i=x;i;i-=lowbit(i))
ans+=c[i];
return ans;
}
int main()
{
char s[30];
int x,y,z,l=1;
cin>>t;
while(t--)
{ mem(c,0);
scanf("%d",&n);
for(int i=1;i<=n;i++)
{scanf("%d",&z);
update(i,z);}
printf("Case %d:\n",l++);
while(1)
{
scanf("%s",s);
if(s[0]=='E')
break;
scanf("%d%d",&x,&y);
if(s[0]=='Q')
printf("%d\n",getsum(y)-getsum(x-1));
else {if(s[0]=='A')
update(x,y);
else update(x,-y);}
}
}
return 0;
}
2.Lost Cows
传送门
题目大意:根据描述,前面有几个比自己小的,然后输出队列的排序
贴代码
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<queue>
#include<stack>
#include<deque>
#include<map>
#include<set>
#include<cstdio>
#include<vector>
#define ll long long
#define mem(a,b) memset(a,b,sizeof(a))
const ll INF=0x3f3f3f3f;
const ll Max=8500;
using namespace std;
int i,j,k,t,n,m;
int a[Max];
int tree[35000];
/*queue<ll> q;
stack<ll> s;
//升序队列
priority_queue <int,vector<int>,greater<int> > q;
//降序队列
priority_queue <int,vector<int>,less<int> >q;*/
void build(int l,int r,int s)
{
tree[s]=r-l+1;
if(l==r)
{
return ;
}
int mid=(l+r)>>1;
build(l,mid,s<<1);
build(mid+1,r,s<<1|1);
}
int query(int l,int r,int s,int num)
{
tree[s]--;
if(l==r)
return l;
int mid=(l+r)>>1;
if(tree[s<<1]>=num)
return query(l,mid,s<<1,num);
else return query(mid+1,r,s<<1|1,num-tree[s<<1]);
}
int main()
{
while(~scanf("%d",&t))
{for(i=2;i<=t;i++)
scanf("%d",&a[i]);
a[1]=0;
build(1,t,1);
for(i=t;i>=1;i--)
a[i]=query(1,t,1,a[i]+1);
for(i=1;i<=t;i++)
printf("%d\n",a[i]);
}return 0;
}
也可用树状数组做
3.Just a Hook
传送门
题目大意:首先一开始给出总的区间长度,每个都为1,然后更改某个区间的所有数,然后输出总和
贴代码
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<queue>
#include<stack>
#include<deque>
#include<map>
#include<set>
#include<cstdio>
#include<vector>
#define ll long
#define mem(a,b) memset(a,b,sizeof(a))
const ll INF=0x3f3f3f3f;
const ll Max=200000+5;
using namespace std;
ll i,j,k,t,n,m;
/*queue<ll> q;
stack<ll> s;
//升序队列
priority_queue <int,vector<int>,greater<int> > q;
//降序队列
priority_queue <int,vector<int>,less<int> >q;*/
struct node {
ll l,r,k,lz;
} tree[Max<<2];
void push_up(ll p){
tree[p].k=tree[p<<1].k+tree[(p<<1)|1].k;
}
void push_down(ll p){
if (tree[p].lz){
tree[p<<1].k=tree[p].lz*(tree[p<<1].r-tree[p<<1].l+1);
tree[(p<<1)|1].k=tree[p].lz*(tree[(p<<1)|1].r-tree[(p<<1)|1].l+1);
tree[p<<1].lz=tree[p].lz;
tree[(p<<1)|1].lz=tree[p].lz;
tree[p].lz=0;
}
}
void build(ll p, ll l, ll r) {
tree[p].l=l;
tree[p].r=r;
tree[p].lz=0; //重置 lazy tag
if (l==r) {
tree[p].k=1;
return;
}
ll m=(l+r)>>1;
build(p<<1,l,m),build((p<<1)|1,m+1,r);
push_up(p);
}
void update(ll p, ll x, ll y, ll z){
if (x<=tree[p].l && y>=tree[p].r){
tree[p].k=z*(tree[p].r-tree[p].l+1);
tree[p].lz=z;
return;
}
push_down(p);
ll m=(tree[p].l+tree[p].r)>>1;
if (x<=m) update(p<<1,x,y,z);
if (y>m) update((p<<1)|1,x,y,z);
push_up(p);
}
ll query(ll p, ll x, ll y) {
if (x<=tree[p].l && y>=tree[p].r) return tree[p].k;
push_down(p);
ll m=(tree[p].l+tree[p].r)>>1;
ll ans=0;
if (x<=m) ans+=query(p<<1,x,y);
if (y>m) ans+=query((p<<1)|1,x,y);
return ans;
}
int main()
{ ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int T;
cin>>T;
int ca=1;
while (T--){
cin>>n>>m;
build(1,1,n);
ll X,Y,Z;
while (m--){
cin>>X>>Y>>Z;
update(1,X,Y,Z);
}
cout<<"Case "<<ca++<<": The total value of the hook is "<<query(1,1,n)<<"."<<endl;
}
}
4.I Hate It
传送门
查询,更改,更新的是某区间的最大值
贴代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#include<map>
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
using namespace std;
const int maxn=222222;
int MAX[maxn<<2];
void PushUp(int rt)
{
MAX[rt]=max(MAX[rt<<1],MAX[rt<<1|1]);
}
void build(int l,int r,int rt)
{
if(l==r)
{
scanf("%d",&MAX[rt]);
return ;
}
int m=(l+r)>>1;
build(lson);
build(rson);
PushUp(rt);
}
void update(int p,int temp,int l,int r,int rt)
{
if(l==r)
{
MAX[rt]=temp;
return;
}
int m=(l+r)>>1;
if(p<=m)
update(p,temp,lson);
else
update(p,temp,rson);
PushUp(rt);
}
int query(int L,int R,int l,int r,int rt)
{
if(L<=l&&r<=R)
{
return MAX[rt];
}
int m=(l+r)>>1;
int ret=0;
if(L<=m) ret=max(ret,query(L,R,lson));
if(R>m) ret=max(ret,query(L,R,rson));
return ret;
}
int main()
{
int n,m;
while(~scanf("%d%d",&n,&m))
{
build(1,n,1);
while(m--)
{
char op[2];
int a,b;
scanf("%s%d%d",op,&a,&b);
if(op[0]=='Q')
printf("%d\n",query(a,b,1,n,1));
else
update(a,b,1,n,1);
}
}
return 0;
}
5.Ultra-QuickSort
传送门
排序问题,只能相邻的交换,可以用树状数组,我用的是归序排序参考
贴代码
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<queue>
#include<stack>
#include<deque>
#include<map>
#include<set>
#include<cstdio>
#include<vector>
#define ll long long
#define mem(a,b) memset(a,b,sizeof(a))
const ll INF=0x3f3f3f3f;
const ll Max=500010;
using namespace std;
int i,j,k,t,n,m,l;
ll sum;
int num[Max],tmp[Max];
/*queue<ll> q;
stack<ll> s;
//升序队列
priority_queue <int,vector<int>,greater<int> > q;
//降序队列
priority_queue <int,vector<int>,less<int> >q;*/
void merge(int low, int mid, int high)
{
i=k=low;
j=mid+1;
while(i<=mid && j<=high)
{
if(num[i]<num[j])
tmp[k++]=num[i++];
else
{
sum+=j-k;
tmp[k++]=num[j++];
}
}
while(i<=mid)
tmp[k++]=num[i++];
while(j<=high)
tmp[k++]=num[j++];
for(i=low; i<=high; ++i)
num[i]=tmp[i];
}
void mergeSort(int a, int b)
{
int mid;
if(a<b)
{
mid=(a+b)/2;
mergeSort(a, mid);
mergeSort(mid+1, b);
merge(a, mid, b);
}
}
int main()
{
while(scanf("%d",&n)&&n)
{ sum=0;
for(i=0;i<n;i++)
scanf("%d",&num[i]);
mergeSort(0,n-1);
printf("%lld\n",sum);
}
return 0;
}
6.Buy Tickets
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<queue>
#include<stack>
#include<deque>
#include<map>
#include<set>
#include<cstdio>
#include<vector>
#define ll long long
#define mem(a,b) memset(a,b,sizeof(a))
const int INF=0x3f3f3f3f;
const int Max=200010;
using namespace std;
int i,j,k,t,n,m;
int a[Max],b[Max];
int tree[Max<<2];
int sum[Max];
/*queue<ll> q;
stack<ll> s;
//升序队列
priority_queue <int,vector<int>,greater<int> > q;
//降序队列
priority_queue <int,vector<int>,less<int> >q;*/
void build(int k,int l,int r)
{
tree[k]=r-l+1;
if(l==r)return ;
int mid=(l+r)>>1;
build(2*k,l,mid);
build(2*k+1,mid+1,r);
}
int update(int k,int x,int l,int r)
{
tree[k]--;
if(l==r)return l;
int mid=(r+l)>>1;
if(x<=tree[k<<1]) return update(k<<1,x,l,mid);
else return update(k<<1|1,x-tree[k<<1],mid+1,r);
}
int main()
{
while(scanf("%d",&t)!=EOF)
{ mem(tree,0);
for(i=0;i<t;i++)
scanf("%d%d",&a[i],&b[i]);
build(1,1,t);
for(i=t-1;i>=0;i--)
{
n=update(1,a[i]+1,1,t);
sum[n]=b[i];
}
for(i=1;i<=t;i++)
printf("%d ",sum[i]);
cout<<endl;
}
return 0;
}
7.Stars
#include<iostream>
#include<algorithm>
using namespace std;
int n,m,i,num[32005],t[32005],l,r;//num:原数组;t:树状数组
int lowbit(int x)
{
return x&(-x);
}
void change(int x,int p)//将第x个数加p
{ if(x==0)return ;
while(x<=32001)
{
t[x]+=p;
x+=lowbit(x);
}
return;
}
int sum(int k)//前k个数的和
{
int ans=0;
while(k>0)
{
ans+=t[k];
k-=lowbit(k);
}
return ans;
}
int ask(int l,int r)//求l-r区间和
{
return sum(r)-sum(l-1);
}
int main()
{
cin>>n;
int x,y;
for(i=0;i<n;i++)
{
scanf("%d%d",&x,&y);
x++;
num[sum(x)]++;
change(x,1);
}
for(i=0;i<n;i++)
printf("%d\n",num[i]);
return 0;
}
8.A Simple Problem with Integers
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<queue>
#include<stack>
#include<deque>
#include<map>
#include<set>
#include<cstdio>
#include<vector>
#define ll long long
#define mem(a,b) memset(a,b,sizeof(a))
const ll INF=0x3f3f3f3f;
const ll Max=100000+5;
using namespace std;
ll i,j,k,t,n,m;
ll num[Max*4];
/*queue<ll> q;
stack<ll> s;
//升序队列
priority_queue <int,vector<int>,greater<int> > q;
//降序队列
priority_queue <int,vector<int>,less<int> >q;*/
void change_segment(ll k,ll l,ll r,ll x);
void pushdown(ll k);
struct node
{
ll l/*区间左边界*/,r/*区间右边界*/,sum/*区间和*/,maxx/*区间最大值*/,minn/*区间最小值*/,lazy/*懒惰标记*/;
node()
{
l=r=sum=lazy=maxx=minn=0;
}
}a[Max*4];//N为总节点数
void update(ll k)//更新节点k的信息
{ pushdown(k);
a[k].sum=a[k*2].sum+a[2*k+1].sum;
//很显然,一段区间的元素和等于它的子区间的元素和
//如果有懒惰标记的话要相应地改变(记得加上懒惰标记的值!!!)
}
void build(ll k,ll l,ll r)
{
a[k].l=l;a[k].r=r;
if(l==r)//递归到了叶节点
{
a[k].sum+=num[l];
return ;
}
ll mid=(l+r)/2;//计算左右子节点的分界
build(k*2,l,mid);//递归到左子节点
build(2*k+1,mid+1,r);//递归到右子节点
update(k);//用左右子节点更新父节点
}
//单点修改
void change_point(ll k/*当前节点编号*/,ll x/*要修改的编号*/,ll y/*要修改为的数*/)
{
if(a[k].l==a[k].r) {a[k].sum+=y;return ;}
ll mid=(a[k].l+a[k].r)/2;
if(x<=mid) change_point(k*2,x,y);//递归到左子节点
else change_point(2*k+1,x,y);//递归到右子节点
update(k);//更新k节点的数据
}
//区间修改
//相对标记指的是可以共存的标记,且打标记的顺序与答案无关,即标记可以叠加。 比如说给一段区间中的所有数字都 + a +a+a ,我们就可以把标记叠加一下,比如上一次打了一个 + 1 +1+1 的标记,这一次要给这一段区间 + 5 +5+5 ,那么就把 + 1 +1+1 的标记变成 + 6 +6+6 。
void change_segment(ll k,ll l,ll r,ll x)
//将[l,r]区间进行修改,x用于修改的
{
if(a[k].l==l&&a[k].r==r)//如果找到全部元素的区间
{
a[k].sum+=(r-l+1)*x;
//更新区间的数据
a[k].lazy+=x;return ;
//懒惰标记叠加
}
ll mid=(a[k].l+a[k].r)/2;//区间完全在左子节点的区间内
if(r<=mid) change_segment(k*2,l,r,x);
else if(l>mid) change_segment(k*2+1,l,r,x);//区间完全在右子节点的区间内
else change_segment(k*2,l,mid,x),change_segment(k*2+1,mid+1,r,x);
update(k);
//更新数据
}
//绝对标记是指不可以共存的标记,每一次都要先把标记下传,再给当前节点打上新的标记。这些标记不能改变次序,否则会出错。 比如说给一段区间的数字重新赋值,或是给一段区间进行多种操作
//请注意:某些题目的懒惰标记属于 绝对标记 (如维护区间 平方和 ),一定要先 下传标记 ,再向下递归。
void pushdown(ll k)//将节点k的懒惰标记下传
{
if(a[k].l==a[k].r){a[k].lazy=0;return ;}
//如果该节点是叶,则不需要下传,直接返回就可
a[k*2].sum+=(a[k*2].r-a[k*2].l+1)*a[k].lazy;
a[k*2+1].sum+=(a[k*2+1].r-a[k*2+1].l+1)*a[k].lazy;
//给节点k的子节点重新赋值
a[k*2].lazy+=a[k].lazy;
a[k*2+1].lazy+=a[k].lazy;
//下传节点k的标记
a[k].lazy=0;//清空节点k的标记
}
//记得在查询之前下传标记!!!
ll query(ll k,ll l,ll r)
//当前到达编号为k的节点,查寻[l.r]的和
{
if(a[k].lazy) pushdown(k);
//如果当前节点被打上了懒惰标记,那么就把这个标记下传,这一句其实也可以放在下一语句的后面
if(a[k].l==l&&a[k].r==r) return a[k].sum;
//如果当前区间就是询问区间,完全重合,那么显然可以直接返回
ll mid=(a[k].l+a[k].r)/2;
if(r<=mid) return query(k*2,l,r);
//如果询问区间包含在左子区间中
if(l>mid) return query(k*2+1,l,r);
//如果询问区间包含在右子区间中
return query(k*2,l,mid)+query(k*2+1,mid+1,r);
//如果询问区间跨越两个子区间
}
int main()
{
while(~scanf("%lld%lld",&n,&m))
{
for(i=1;i<=n;i++)
{
scanf("%lld",&num[i]);
getchar();
}
build(1,1,n);
char b;
while(m--)
{ scanf("%c",&b);
if(b=='Q')
{ ll L,R;
scanf("%lld%lld",&L,&R);
ll w=query(1,L,R);
printf("%lld\n",w);
}
else if(b=='C')
{
ll add,q1,q2;
scanf("%lld%lld%lld",&q1,&q2,&add);
change_segment(1,q1,q2,add);
}
getchar();
}
mem(a,0);
}
return 0;
}