只是普通线段树,并非什么可持久化,主席树。。。。ORZ
最近写了些水题,挑了其中的几道,发一下。感觉对我这样的蒟蒻来说,线段树的某些模型还是需要学习的。。。
(XX根本想不到)
农夫约翰的 N (1 ≤ N ≤ 50,000) 头奶牛,每天挤奶时总会按同样的顺序站好。一日,农夫约翰决定为奶牛们举行一个“终极飞盘”比赛。为简化问题,他将从奶牛队列中选出一个连续区间来进行游戏。不过,参加游戏的奶牛要玩的开心的话就不能在身高上差距太大。
农夫约翰制定了 Q (1 ≤ Q ≤ 200,000) 个预定的参赛组,给出它们的身高 (1 ≤ 身高 ≤ 1,000,000)。对每个参赛组,他需要你帮助确定组中最高牛和最低牛的身高差。
不贴代码了,水,分别维护最大值、最小值即可。
某次列车途经C个城市,城市编号依次为1到C,列车上共有S个座位,铁路局规定售出的车票只能是坐票, 即车上所有的旅客都有座。售票系统是由计算机执行的,每一个售票申请包含三个参数,分别用O、D、N表示,O为起始站,D为目的地站,N为车票张数。售票 系统对该售票申请作出受理或不受理的决定,只有在从O到D的区段内列车上都有N个或N个以上的空座位时该售票申请才被受理。请你写一个程序,实现这个自动 售票系统。
第一行包含三个用空格隔开的整数C、S和R,其中1≤C≤60000, l≤S≤60000,1≤R≤60000。C为城市个数,S为列车上的座位数,R为所有售票申请总数。接下来的R行每行为一个售票申请,用三个由空格隔开的整数O,D和N表示,O为起始站,D 为目的地站,N为车票张数,其中1≤D≤C,1≤O≤C,所有的售票申请按申请的时间从早到晚给出。
直接模拟,整个lazy标记
#include
#include
#include
#include
using namespace std;
int n,m,k,l,num,ans,c,s,r,o,d,lazy[60005*4];
struct wf{
int l,r,w;
}tree[60005*4];
void build(int l,int r,int root)
{
tree[root].l=l;
tree[root].r=r;
if (l==r)
{
tree[root].w=s;
return;
}
build(l,l+r>>1,root<<1);
build((l+r>>1)+1,r,root<<1|1);
tree[root].w=min(tree[root<<1].w,tree[root<<1|1].w);
}
void pushdown(int root)
{
if (lazy[root]<0)
{
lazy[root<<1]+=lazy[root];
lazy[root<<1|1]+=lazy[root];
tree[root<<1].w+=lazy[root];
tree[root<<1|1].w+=lazy[root];
lazy[root]=0;
}
}
void change(int l,int r,int v,int root)
{
if (tree[root].l>=l&&tree[root].r<=r)
{
lazy[root]-=v;
tree[root].w-=v;
return;
}
if (tree[root].l>r||tree[root].r=l&&tree[root].r<=r)
{
return tree[root].w;
}
if (tree[root].l>r||tree[root].r=n)//如果可以受理,就把座位标记上
{
change(o,d-1,n,1);
printf("YES\n");
}
else printf("NO\n");
}
//while(1);
return 0;
}
一个长度为n的序列,一开始序列数的权值都是0,有m次操作
支持两种操作:
1 L R x,给区间[L,R]内位置为pos的数加上(pos-L)*x
0 L R,查询区间[L,R]内的权值和
最终答案对109+7取模。
真的给写跪了qaq,等差数列综合应用啊啊啊啊。+1-1什么的很麻烦。。。。写挂了无数次,一直以为pushdown不对,其实取模才是关键!!!
#include
#include
#include
#include
using namespace std;
struct wf{
long long l,r;
long long w;
}tree[300005*4];
long long fro[300005*4],x,va[300005*4];
long long n,m,k,l,num,ans,r,f,md;
void build(long long l,long long r,long long root)
{
tree[root].l=l;
tree[root].r=r;
if (l==r) return;
build(l,l+r>>1,root<<1);
build((l+r>>1)+1,r,root<<1|1);
}
void pushdown(long long root)
{
if (tree[root].l==tree[root].r) return;
if (va[root])
{
long long mid=(tree[root].l+tree[root].r)>>1;
va[root<<1]=(va[root<<1]+va[root]+md)%md;//va是两项间的差值,fro是首项
va[root<<1|1]=(va[root<<1|1]+va[root]+md)%md;
fro[root<<1]=(fro[root]+fro[root<<1]+md)%md;
fro[root<<1|1]=(md+fro[root<<1|1]+fro[root]+(mid-tree[root].l+1)*va[root])%md;
//tree[root<<1].w+=(fro[root]+fro[root]+(tree[root<<1].r-tree[root].l)*x)*(tree[root<<1].r-tree[root<<1].l+1)/2;
tree[root<<1].w=(long long)(md+tree[root<<1].w+fro[root]*(mid-tree[root].l+1)%md)%md;
tree[root<<1].w=(long long)(md+tree[root<<1].w+((mid-tree[root].l)*va[root])*(mid-tree[root].l+1)/2)%md;
//tree[root<<1|1].w+=(fro[root<<1|1]+fro[root<<1|1]+(tree[root<<1|1].r-tree[root<<1|1].l)*x)*(tree[root<<1|1].r-tree[root<<1|1].l+1)/2;
tree[root<<1|1].w=(long long)(md+tree[root<<1|1].w+(fro[root]+(va[root]*(mid+1-tree[root].l)))*(tree[root].r-mid)%md)%md;
tree[root<<1|1].w=(long long)(md+tree[root<<1|1].w+va[root]*(tree[root].r-mid-1)*(tree[root].r-mid)/2%md)%md;
va[root]=0;//我也知道很长,可我有什么办法。。。
fro[root]=0;
}
}
void change(long long l,long long r,long long x,long long root)
{
if (tree[root].l>=l&&tree[root].r<=r)
{
tree[root].w=(long long)(md+tree[root].w+((tree[root].l-l)*x)*(tree[root].r-tree[root].l+1))%md;
tree[root].w=(long long)(md+tree[root].w+(tree[root].r-tree[root].l)*x*(tree[root].r-tree[root].l+1)/2%md)%md;
fro[root]=(fro[root]+(tree[root].l-l)*x+md)%md;
va[root]=(va[root]+x+md)%md;
return;
}
if (tree[root].l>r||tree[root].r=l&&tree[root].r<=r) return tree[root].w;
if (tree[root].l>r||tree[root].r
很好的一道题,教会了人们如何用线段树求最大连续区间!
维护tree[]的lx:从左端点往右最大连续子段和;rx:从右端点往左最大连续子段和;mx:tree[root].l至tree[root].r的最大连续子段和;sum:tree[root].l至tree[root].r的和。在纸上画下,易知pushup方法
#include
#include
#include
#include
using namespace std;
int n,m,k,l,num,ans,x;
struct wf{
int l,r;
long long mx,lx,rx,sum;
}tree[100005*4];
long long a[100005];
void pushup(int root)
{
tree[root].sum=tree[root<<1].sum+tree[root<<1|1].sum;
tree[root].lx=max(tree[root<<1].lx,tree[root<<1].sum+tree[root<<1|1].lx);
tree[root].rx=max(tree[root<<1|1].rx,tree[root<<1|1].sum+tree[root<<1].rx);
tree[root].mx=max(tree[root<<1].mx,tree[root<<1|1].mx);//自己想下
tree[root].mx=max(tree[root].mx,tree[root<<1].rx+tree[root<<1|1].lx);
}
void build(int l,int r,int root)
{
tree[root].l=l;
tree[root].r=r;
if (l==r)
{
tree[root].lx=a[l];
tree[root].rx=a[l];
tree[root].mx=a[l];
tree[root].sum=a[l];
return;
}
build(l,l+r>>1,root<<1);
build((l+r>>1)+1,r,root<<1|1);
pushup(root);
}
void change(int x,int root)
{
if (tree[root].l==tree[root].r&&tree[root].l==x)
{
tree[root].lx=-199999999999999;
tree[root].rx=-199999999999999;
tree[root].mx=-199999999999999;
tree[root].sum=-199999999999999;//这道题最坑之处就在于设INF.....
return;
}
if (tree[root].l>x||tree[root].r
给定一个数列,有如下两种操作:
1:将其中的一个数加上s(s为整数)
2:给定区间[l,r],求al⋅(r−l+1)+al+1⋅(r−l)+......+ar−1⋅2+ar⋅1的值。
即:
∑ri=lai⋅(r−i+1)
第一行有2个数n,q,分别表示Teacher数列中数的个数以及操作次数。
接下来的一行有n个数,第i个数表示ai。
再接下来q行,每行三个数;第一个数是order。如果order=1,那么接下来两个数:x, s,即把ax加上s;如果order=2,那么接下来两个数:l, r,即求这一段区间源氏要求的答案。
对于每一个询问(order=2)输出所求答案
不难发现,ai的系数呈公差为一的等差,我们可以直接按l=1,r=n建树,之后再减去某一段和的倍数。
相当于求出一个梯形,再减去一个矩形,这样才完美地得到一个三角形
#include
#include
#include
#include
typedef long long LL;
using namespace std;
int n,x,f,q,l,r;
LL a[100005],ans1,ans2,s;
struct wf{
int l,r;
LL num,sum;
}tree[100005*4];
inline LL read()//为了刷榜还上网copy了读入优化。。。
{
int X=0,w=0; char ch=0;
while(!isdigit(ch)) {w|=ch=='-';ch=getchar();}
while(isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
return w?-X:X;
}
void pushup(int root)
{
tree[root].num=tree[root<<1].num+tree[root<<1|1].num;
tree[root].sum=tree[root<<1].sum+tree[root<<1|1].sum;
}
void build(int l,int r,int root)
{
tree[root].l=l;
tree[root].r=r;
if (l==r)
{
tree[root].num=a[l];
tree[root].sum=a[l]*(n-l+1);
return;
}
build(l,l+r>>1,root<<1);
build((l+r>>1)+1,r,root<<1|1);
pushup(root);
}
void change(int x,LL s,int root)
{
if (tree[root].l==tree[root].r&&tree[root].l==x)
{
tree[root].num+=s;
tree[root].sum=tree[root].num*(n-x+1);
return;
}
if (tree[root].l>x||tree[root].rr||tree[root].r=l&&tree[root].r<=r) return tree[root].sum;
return asksum(l,r,root<<1)+asksum(l,r,root<<1|1);
}
LL asknum(int l,int r,int root)
{
if (tree[root].l>r||tree[root].r=l&&tree[root].r<=r) return tree[root].num;
return asknum(l,r,root<<1)+asknum(l,r,root<<1|1);
}
int main()
{
freopen("overwatch.in","r",stdin);
freopen("overwatch.out","w",stdout);
n=read();
q=read();
for (int i=1;i<=n;i++)
a[i]=read();
build(1,n,1);
for (int i=1;i<=q;i++)
{
f=read();
if (f==1)
{
x=read();
s=read();
change(x,s,1);
}
else
{
l=read();
r=read();
ans1=asksum(l,r,1);//梯形
ans2=asknum(l,r,1)*(n-r);//长乘宽出来的矩形
printf("%lld\n",ans1-ans2);
}
}
//while(1);
return 0;
}
一个简单的数列问题:
给定一个长度为n的数列,求这样的三个元素 ai,aj,ak 的个数,
满足 ai<aj>ak,且 i<j<k。
第1行是一个整数n(1<=n<=50000)。
接下来n行,每行一个元素ai(0<=ai<=32767)
输出:一个数,满足 ai<aj>ak (i<j<k) 的个数。
好题啊啊(对我来说也很难...)
首先,枚举中间的那个
一眼看到n,想用n建树。很长时间后———这不可能!
咦,a这么小,按a建树吧!这样问题就简化为了区间求和?!这时,要考律一些东西了:a[i]插入线段树前只有a[i]之前的,但根据乘法原理,必须同时统计到a[i]以后的。这可怎么办?=v=
没关系,建两颗线段树,正反两遍插入,令开两个数组统计,最后再枚举中间点相乘相加不就行了?
#include
#include
#include
#include
typedef long long LL;
int n,m,k;
LL ans,a[50005],l[50005],r[50005];
struct wf{
int l,r;
LL w;
}tree[3][50005*4];
using namespace std;
void build(int num,int l,int r,int root)
{
tree[num][root].l=l;
tree[num][root].r=r;
if (l==r) return;
build(num,l,l+r>>1,root<<1);
build(num,(l+r>>1)+1,r,root<<1|1);
}
LL ask(int num,int l,int r,int root)
{
if (tree[num][root].l>=l&&tree[num][root].r<=r) return tree[num][root].w;
if (tree[num][root].l>r||tree[num][root].rx||tree[num][root].r=1;i--)//正反循环插入
{
r[i]=ask(2,0,a[i]-1,1);
charu(2,a[i],1);
}
for (int i=1;i<=n;i++)//枚举中间点
ans+=l[i]*r[i];
printf("%lld",ans);
//while(1);
return 0;
}
AKN觉得第一题太水了,不屑于写第一题,所以他又玩起了新的游戏。在游戏中,他发现,这个游戏的伤害计算有一个规律,规律如下
1、 拥有一个伤害串为长度为n的01串。2、 给定一个范围[l,r],伤害为伤害串的这个范围内中1的个数3、 会被随机修改伤害串中的数值,修改的方法是把[l,r]中的所有数xor上1
AKN想知道一些时刻的伤害,请你帮助他求出这个伤害
第一行两个数n,m,表示长度为n的01串,有m个时刻
第二行一个长度为n的01串,为初始伤害串
第三行开始m行,每行三个数p,l,r
若p为0,则表示当前时刻改变[l,r]的伤害串,改变规则如上
若p为1,则表示当前时刻AKN想知道[l,r]的伤害
输出格式:对于每次询问伤害,输出一个数值伤害,每次询问输出一行
xor1是什么,01串取反对么,就是1变0,0变1!!!
同时,x^a^a==x,取反再取反等于不变,优化。
#include
#include
#include
#include
using namespace std;
char s[200005];
int n,m,p,r,l;
int lazy[200005*4];
struct wf{
int v,r,l;
}t[200005*4];
void build(int x,int y,int num)
{
t[num].l=x;
t[num].r=y;
if (x==y)
{
t[num].v=s[x]-'0';
//printf("%d ",t[num].v);
return;
}
build(x,(x+y)/2,num*2);
build((x+y)/2+1,y,num*2+1);
t[num].v=t[num*2].v+t[num*2+1].v;
}
void change(int x,int y,int root)
{
if (t[root].l>=x&&t[root].r<=y)
{
lazy[root]^=1;
t[root].v=t[root].r-t[root].l+1-t[root].v;
return;
}
if (t[root].ry) return;
if (lazy[root])
{
lazy[root*2]^=1;
lazy[root*2+1]^=1;
t[root*2].v=t[root*2].r-t[root*2].l+1-t[root*2].v;
t[root*2+1].v=t[root*2+1].r-t[root*2+1].l+1-t[root*2+1].v;
lazy[root]=0;
}
change(x,y,root*2);
change(x,y,root*2+1);
t[root].v=t[root*2].v+t[root*2+1].v;
}
int ask(int x,int y,int root)
{
if (t[root].l>y||t[root].r=x&&t[root].r<=y) return t[root].v;
if (lazy[root])
{
lazy[root*2]^=1;
lazy[root*2+1]^=1;
t[root*2].v=t[root*2].r-t[root*2].l+1-t[root*2].v;//区间内01数量交换啦
t[root*2+1].v=t[root*2+1].r-t[root*2+1].l+1-t[root*2+1].v;
lazy[root]=0;
}
return ask(x,y,root*2)+ask(x,y,root*2+1);
}
int main()
{
scanf("%d%d",&n,&m);
scanf("%s",s+1);
build(1,n,1);
for (int i=1;i<=m;i++)
{
scanf("%d%d%d",&p,&l,&r);
if (p==0)
{
change(l,r,1);
}
else
{
printf("%d\n",ask(l,r,1));
}
}
return 0;
}
先乘后加线段树模板嘛。其中,很多东西要考律到,注释不清楚的
#include
#include
#include
#include
using namespace std;
long long cur[1000005*4],a[1000005],k;
struct wf{
long long add,ch;
}lazy[1000005*4];
int n,m,l,num,ans,p,x,y,f;
void pushdown(int root,int l,int r)//巨麻烦
{
int mid=(l+r)/2;
cur[root<<1]=(cur[root<<1]*lazy[root].ch+lazy[root].add*(mid-l+1))%p;
cur[(root<<1)+1]=(cur[(root<<1)+1]*lazy[root].ch+lazy[root].add*(r-mid))%p;
lazy[root<<1].ch=(lazy[root].ch*lazy[root<<1].ch)%p;
lazy[(root<<1)+1].ch=(lazy[root].ch*lazy[(root<<1)+1].ch)%p;
lazy[root<<1].add=(lazy[root<<1].add*lazy[root].ch+lazy[root].add)%p;
lazy[(root<<1)+1].add=(lazy[(root<<1)+1].add*lazy[root].ch+lazy[root].add)%p;
lazy[root].ch=1;
lazy[root].add=0;
}
void add1(int root,int nl,int nr,int l,int r)
{
if (nl>r||nrr||nrr||nr
XX:sequence
定义函数f(x) = kx + b。
现给定n 个这样的函数,并且需要你支持以下操作:
M i k b 修改第i 个函数的k 值与b 值
Q l r x 询问第fr(fr-1(fr-2(…(fl(x))))) 对1e9+7 取模后的结果
每个函数的k 值与b 值可能会不一样,且会在输入中给出。
第一行两个整数n 和m,表示n 个函数以及m 次操作。
接下来n 行,每行两个整数,表示ki,bi。
接下来m 行,每行表示一个操作,操作格式如上述所示。
m 行表示答案。
一道考试题,很遗憾,考场上没写出。。。当时想到了线段树,但脑子抽了,不认为它可合并
K(kx+b)+B=Kkx+Kb+B>>>>k'=Kk;b'=Kb+B orz orz orz qwq 这份代码没交,不保证正确!
#include
#include
#include
#include
using namespace std;
int n,m,l,r,p;
long long num,x,k[200005],b[200005],w,ww;
char s[10];
struct wf{
int l,r;
long long k,b;
}tree[200005*4];
void build(int l,int r,int root)
{
tree[root].l=l;
tree[root].r=r;
if (l==r)
{
tree[root].k=k[l];
tree[root].b=b[l];
return;
}
build(l,(l+r)>>1,root<<1);
build(((l+r)>>1)+1,r,(root<<1)+1);
tree[root].k=(tree[root<<1].k*tree[(root<<1)+1].k)%1000000007;
tree[root].b=(tree[(root<<1)+1].k*tree[root<<1].b+tree[(root<<1)+1].b)%1000000007;
}
void change(int x,int root)
{
if (tree[root].l==tree[root].r)
{
tree[root].k=w;
tree[root].b=ww;
return;
}
if (tree[root].l>x||tree[root].r=l&&tree[root].r<=r)
{
return tree[root].k;
}
if (tree[root].l>r||tree[root].r=l&&tree[root].r<=r)
{
return tree[root].b;
}
int mid=tree[root].l+tree[root].r>>1;
if (r<=mid) return askb(l,r,root<<1);
else if (l>mid) return askb(l,r,(root<<1)+1);
return (askk(l,r,(root<<1)+1)*askb(l,r,root<<1)+askb(l,r,(root<<1)+1))%1000000007;//如上
}
int main()
{
//freopen("sequence.in","r",stdin);
//freopen("sequence.out","w",stdout);
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++)
scanf("%lld%lld",&k[i],&b[i]);
build(1,n,1);
for (int i=1;i<=m;i++)
{
scanf("%s",s);
if (s[0]=='Q')
{
scanf("%d%d%lld",&l,&r,&x);
printf("%lld\n",(askk(l,r,1)*x+askb(l,r,1))%1000000007);
}
else
{
scanf("%d%lld%lld",&p,&w,&ww);
change(p,1);
}
}
while(1);
return 0;
}
先写到这里吧,好困,有时间再写一点。如果你们有好题,欢迎分享!