线段树水题集锦(持续更新)

只是普通线段树,并非什么可持久化,主席树。。。。ORZ

最近写了些水题,挑了其中的几道,发一下。感觉对我这样的蒟蒻来说,线段树的某些模型还是需要学习的。。。

(XX根本想不到)

cogs:182. [USACO Jan07] 均衡队形

农夫约翰的 N (1 ≤ N ≤ 50,000) 头奶牛,每天挤奶时总会按同样的顺序站好。一日,农夫约翰决定为奶牛们举行一个“终极飞盘”比赛。为简化问题,他将从奶牛队列中选出一个连续区间来进行游戏。不过,参加游戏的奶牛要玩的开心的话就不能在身高上差距太大。

农夫约翰制定了 Q (1 ≤ Q ≤ 200,000) 个预定的参赛组,给出它们的身高 (1 ≤ 身高 ≤ 1,000,000)。对每个参赛组,他需要你帮助确定组中最高牛和最低牛的身高差。


不贴代码了,水,分别维护最大值、最小值即可。

cogs:247. 售票系统

某次列车途经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<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
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)
	{
		return;
	}
	change(l,r,v,root<<1);
	change(l,r,v,root<<1|1);
	pushdown(root);//pushdown位置把握好
	tree[root].w=min(tree[root<<1].w,tree[root<<1|1].w);
}

int ask(int l,int r,int root)
{
	if (tree[root].l>=l&&tree[root].r<=r)
	{
		return tree[root].w;
	}
	if (tree[root].l>r||tree[root].r<l)
	{
		return 99999999;
	}
	pushdown(root);
	return min(ask(l,r,root<<1),ask(l,r,root<<1|1));
}
int main()
{
	freopen("railway.in","r",stdin);
	freopen("railway.out","w",stdout);
	scanf("%d%d%d",&c,&s,&r);
	build(1,c-1,1);
	for (int i=1;i<=r;i++)
	{
		scanf("%d%d%d",&o,&d,&n);
		if (ask(o,d-1,1)>=n)//如果可以受理,就把座位标记上
		{
			change(o,d-1,n,1);
			printf("YES\n");
		}
		else printf("NO\n");
	}
	//while(1);
	return 0;
}

cogs:2632. [HZOI 2016] 数列操作d(2264几乎一样)

一个长度为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<algorithm>
#include<cstdio>
#include<cstring>
#include<iostream>
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) return;
	pushdown(root);
	if (tree[root].r!=tree[root].l)
	{
        change(l,r,x,root<<1);
	    change(l,r,x,root<<1|1);
	    tree[root].w=(tree[root<<1].w+tree[root<<1|1].w+md)%md;
	}
}
long long ask(long long l,long long r,long long root)
{
	if (tree[root].l>=l&&tree[root].r<=r) return tree[root].w;
	if (tree[root].l>r||tree[root].r<l) return 0;
	pushdown(root);
	if (tree[root].l!=tree[root].r) return (ask(l,r,root<<1)+ask(l,r,root<<1|1)+md)%md;
}
int main()
{
	freopen("segment.in","r",stdin);
	freopen("segment.out","w",stdout);//真~无脑模拟?
	md=1000000007;
	scanf("%lld%lld",&n,&m);
	build(1,n,1);
	for (long long i=1;i<=m;i++)
	{
		scanf("%lld",&f);
		if (f==1)
		{
			scanf("%lld%lld%lld",&l,&r,&x);
			change(l,r,x,1);
		}
		else
		{
			scanf("%lld%lld",&l,&r);
			printf("%lld\n",ask(l,r,1));
		}
	}
	//while(1);
	return 0;
}

cogs:2514. 艺术

很好的一道题,教会了人们如何用线段树求最大连续区间!

维护tree[]的lx:从左端点往右最大连续子段和;rx:从右端点往左最大连续子段和;mx:tree[root].l至tree[root].r的最大连续子段和;sum:tree[root].l至tree[root].r的和。在纸上画下,易知pushup方法

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
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<x) return;
	change(x,root<<1);
	change(x,root<<1|1);
	pushup(root);
}
int main()
{
	freopen("art.in","r",stdin);
	freopen("art.out","w",stdout);
	scanf("%d",&n);
	for (int i=1;i<=n;i++)
	  scanf("%lld",&a[i]);
	build(1,n,1);
	for (int i=1;i<n;i++)
	{
		scanf("%d",&x);
		change(x,1);
		printf("%lld\n",tree[1].mx);
	}
	printf("0");
	//while(1);
	return 0;
}

cogs:471. [EZOI 2016]源氏的数学课

给定一个数列,有如下两种操作:

1:将其中的一个数加上s(s为整数)

2:给定区间[l,r],求al(rl+1)+al+1(rl)+......+ar12+ar1的值。

即:

ri=lai(ri+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<algorithm>
#include<cstring>
#include<iostream>
#include<cstdio>
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].r<x) return;
	change(x,s,root<<1);
	change(x,s,root<<1|1);
	pushup(root);
}
LL asksum(int l,int r,int root)
{
	if (tree[root].l>r||tree[root].r<l) return 0;
	if (tree[root].l>=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) return 0;
	if (tree[root].l>=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;
}

cogs:859. 数列

一个简单的数列问题:

给定一个长度为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<algorithm>
#include<cstring>
#include<iostream>
#include<cstdio>
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].r<l) return 0;
	return ask(num,l,r,root<<1)+ask(num,l,r,root<<1|1);
}
void charu(int num,int x,int root)
{
	if (tree[num][root].l==tree[num][root].r&&tree[num][root].l==x)
	{
		tree[num][root].w++;
		return;
	}
	if (tree[num][root].l>x||tree[num][root].r<x) return;
	charu(num,x,root<<1);
	charu(num,x,root<<1|1);
	tree[num][root].w=tree[num][root<<1].w+tree[num][root<<1|1].w;
}
int main()
{
	freopen("queueb.in","r",stdin);
	freopen("queueb.out","w",stdout);
	scanf("%d",&n);
	build(1,0,32768,1);
	build(2,0,32768,1);
	for (int i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
		l[i]=ask(1,0,a[i]-1,1);
		charu(1,a[i],1);//插入,原谅我英文差。。。
	}
	for (int i=n;i>=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;
}

                                           洛谷:P2574 XOR的艺术

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<algorithm>
#include<cstring>
#include<iostream>
#include<cstdio>
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].r<x||t[root].l>y) 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) return 0;
	if (t[root].l>=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;
}

洛谷:P3373 【模板】线段树 2

先乘后加线段树模板嘛。其中,很多东西要考律到,注释不清楚的

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
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||nr<l) return;
	if (l<=nl&&nr<=r)
	{
		cur[root]=(cur[root]*k)%p;
		lazy[root].add=(lazy[root].add*k)%p;
		lazy[root].ch=(lazy[root].ch*k)%p;
		return;
	}
	pushdown(root,nl,nr);
	int mid=(nl+nr)/2;
	add1(root<<1,nl,mid,l,r);
	add1((root<<1)+1,mid+1,nr,l,r);
	cur[root]=(cur[root<<1]+cur[(root<<1)+1])%p;
	return;
}
void add2(int root,int nl,int nr,int l,int r)
{
    if (nl>r||nr<l) return;
    if (l<=nl&&nr<=r)
	{
		cur[root]=(cur[root]+(nr-nl+1)*k)%p;
		lazy[root].add=(lazy[root].add+k)%p;
		return;
	}
	pushdown(root,nl,nr);
	int mid=(nl+nr)/2;
	add2(root<<1,nl,mid,l,r);
	add2((root<<1)+1,mid+1,nr,l,r);
	cur[root]=(cur[root<<1]+cur[(root<<1)+1])%p;
	return;
}
void build(int nw,int l,int r)
{
	lazy[nw].add=0;
	lazy[nw].ch=1;
	if (l==r)
	{
		cur[nw]=a[l];
		return ;
	}
	else
	{
		int mid=(l+r)/2;
		build(nw<<1,l,mid);
		build((nw<<1)+1,mid+1,r);
		cur[nw]=(cur[nw<<1]+cur[(nw<<1)+1])%p;
	}
	return;
}
long long ask(int root,int nl,int nr,int l,int r)
{
    if (nl>r||nr<l) return 0;
    if (l<=nl&&nr<=r) return cur[root];
    pushdown(root,nl,nr);
    int mid=(nl+nr)/2;
    return (ask(root<<1,nl,mid,l,r)+ask((root<<1)+1,mid+1,nr,l,r))%p;
}
int main()
{
	scanf("%d%d%d",&n,&m,&p);
	for (int i=1;i<=n;i++)
	  scanf("%lld",&a[i]);
	build(1,1,n);
	for (int i=1;i<=m;i++)
	{
		scanf("%d",&f);
		if (f==1)
		{
			scanf("%d%d%lld",&x,&y,&k);
			add1(1,1,n,x,y);
			continue;
		}
		if (f==2)
        {
			scanf("%d%d%lld",&x,&y,&k);
			add2(1,1,n,x,y);
			continue;
        }
        if (f==3)
        {
			scanf("%d%d",&x,&y);
			printf("%lld\n",ask(1,1,n,x,y));
			continue;
        }
	}
	//while(1);
	return 0;
}

                                 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<algorithm>
#include<cstring>
#include<iostream>
#include<cstdio>
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<x) return;
	change(x,root<<1);
	change(x,(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;
}
long long askk(int l,int r,int root)//由于我弱,不会数据结构,只能分着查了。。。
{
	if (tree[root].l>=l&&tree[root].r<=r)
	{
		return tree[root].k;
	}
	if (tree[root].l>r||tree[root].r<l)
	{
		return 1;
	}
	return (askk(l,r,root<<1)*askk(l,r,(root<<1)+1))%1000000007;
}
long long askb(int l,int r,int root)
{
    if (tree[root].l>=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;
}

                 


先写到这里吧,好困,有时间再写一点。如果你们有好题,欢迎分享!大笑


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值