树[数据结构]的杂题不多解释

树状数组 1 :单点修改,区间查询

题目描述

输入一个数列A1,A2….An(1<=N<=100000),在数列上进行M(1<=M<=100000)次操作,操作有以下两种:

(1) 格式为C I X,其中C为字符“C”,I和X(1<=I<=N,|X|<=10000)都是整数,表示把把a[I]改为X

(2) 格式为Q L R,其中Q为字符“Q”,L和R表示询问区间为[ L ,R] (1<=L<=R<=N),表示询问A[L]+…+A[R]的值。

输入格式

第一行输入N(1<=N<=100000),表述数列的长度;

接下来N行,每行一个整数(绝对值不超过10000)依次输入每个数;

接下来输入一个整数M(1<=M<=100000),表示操作数量,接下来M行,每行为C I X或者Q L R。

输出格式

对于每个Q L R 的操作输出答案。

input

5
1
2
3
4
5
3
Q 2 3
C 3 9
Q 1 4

output

5
16

数据规模与约定

时间限制:1s
空间限制:256MB

#include
using namespace std;
#define N 100010
int n,a[N],c[N],m,l,r;
char ch;
int lowbit(int x){return x&-x;}
void add(int x,int y){while(x<=n)c[x]+=y,x+=lowbit(x);}
int work(int x)
{
	int sum=0;
	while(x>0) sum+=c[x],x-=lowbit(x);
	return sum;
}
int main()
{
	cin>>n;
	for(int i=1;i<=n;i++){cin>>a[i];add(i,a[i]);}
	cin>>m;
	for(int i=1;i<=m;i++)
	{
		cin>>ch>>l>>r;
		if(ch=='C') add(l,r-a[l]),a[l]=r;
		else cout<<work(r)-work(l-1)<<endl;
	}
	return 0;
}

数星星

题目描述

天文学家经常要检查星星的地图,每个星星用平面上的一个点来表示,每个星星都有坐标。我们定义一个星星的“级别”为给定的星星中不高于它并且不在它右边的星星的数目。天文学家想知道每个星星的“级别”。

                        5
                      *
                    4
                  *
                1       2   3
              *       *   *

例如上图,5号星的“级别”是3(1,2,4这三个星星),2号星和4号星的“级别”为1。

给你一个地图,你的任务是算出每个星星的“级别”。

输入格式

输入的第一行是星星的数目N(1<=N<=60000),接下来的N行描述星星的坐标(每一行是用一个空格隔开的两个整数X,Y,0<=X,Y<=32000)。

星星的位置互不相同。星星的描述按照Y值递增的顺序列出,Y值相同的星星按照X值递增的顺序列出。

输出格式

输出包含N行,一行一个数。第i行是第i个星星的“级别”

input

5
1 1
5 1
7 1
3 3
5 5

output

0
1
2
1
3

数据规模与约定

时间限制:1s
空间限制:256MB

#include
using namespace std;
#define N 100010
int n,c[N],a,b;
int lowbit(int x){return x&-x;}
void add(int x,int y){while(x<32100)c[x]+=y,x+=lowbit(x);}
int work(int x)
{
	int sum=0;
	while(x) sum+=c[x],x-=lowbit(x);
	return sum;
}
int main()
{
	cin>>n;
	for(int i=1;i<=n;i++){cin>>a>>b;a++,b++;cout<<work(a)<<endl;add(a,1);}
	return 0;
}

区间修改单点查询

题目描述

已知一个数列,你需要进行下面两种操作:

1.将某区间每一个数数加上x

2.求出某一个数的值

输入格式

第一行包含两个整数N、M,分别表示该数列数字的个数和操作的总个数。

第二行包含N个用空格分隔的整数,其中第i个数字表示数列第i项的初始值。

接下来M行每行包含2或4个整数,表示一个操作,具体如下:

操作1: 格式:1 x y k 含义:将区间[x,y]内每个数加上k;

操作2: 格式:2 x 含义:输出第x个数的值。

输出格式

输出包含若干行整数,即为所有操作2的结果。

样例数据
input

5 5
1 5 4 2 3
1 2 4 2
2 3
1 1 5 -1
1 3 5 7
2 4

output

6
10

数据规模与约定

时空限制:1000ms,128M

数据规模:

对于30%的数据:N<=8,M<=10

对于70%的数据:N<=10000,M<=10000

对于100%的数据:N<=500000,M<=500000

#include
using namespace std;
#define N 1000100
int n,a[N],c[N],m,b,t,x,y,z;
int lowbit(int x){return x&-x;}
void add(int x,int y){while(x<=n)c[x]+=y,x+=lowbit(x);}
int work(int x)
{
	int sum=0;
	while(x>0) sum+=c[x],x-=lowbit(x);
	return sum;
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	for(int i=1;i<=m;i++)
	{
		scanf("%d",&t);
		if(t==1){scanf("%d%d%d",&x,&y,&z);add(x,z);add(y+1,-z);}
		else {scanf("%d",&x);printf("%d\n",a[x]+work(x));}
	}
	return 0;
}

打鼹鼠

题目描述

SuperBrother在机房里闲着没事干(再对比一下他的NOIP,真是讽刺啊…),于是便无聊地开始玩“打鼹鼠”…

在这个“打鼹鼠”的游戏中,鼹鼠会不时地从洞中钻出来,不过不会从洞口钻进去(鼹鼠真胆大……)。洞口都在一个大小为n(n<=1024)的正方形中。这个正方形在一个平面直角坐标系中,左下角为(0,0),右上角为(n-1,n-1)。洞口所在的位置都是整点,就是横纵坐标都为整数的点。而SuperBrother也不时地会想知道某一个范围的鼹鼠总数。这就是你的任务。

输入格式

每个输入文件有多行。

第一行,一个数n,表示鼹鼠的范围。

以后每一行开头都有一个数m,表示不同的操作:

m=1,那么后面跟着3个数x,y,k(0<=x,y

m=2,那么后面跟着4个数x1,y1,x2,y2(0<=x1<=x2

m=3,表示老师来了,不能玩了。保证这个数会在输入的最后一行。

询问数不会超过10000,鼹鼠数不会超过int。

输出格式

对于每个m=2,输出一行数,这行数只有一个数,即所询问的区域内鼹鼠的个数。

input

4
1 2 2 5
2 0 0 2 3
3

output

5

数据规模与约定

水题一道。 所有数据均为随机生成,包括样例……

时间限制:1s

空间限制:256MB

#include
using namespace std;
int ans,n,m,x,y,xx,yy,k,a[2000][2000];
int lowbit(int x){return x&-x;}
void add(int x,int y,int k)
{
	for(int i=x;i<=n;i+=lowbit(i))
		for(int j=y;j<=n;j+=lowbit(j))
			a[i][j]+=k;
}
int work(int x,int y)
{
	int sum=0;
	for(int i=x;i>0;i-=lowbit(i))
		for(int j=y;j>0;j-=lowbit(j))
			sum+=a[i][j];
	return sum;
}
int main()
{
	scanf("%d",&n);
	while(1)
	{
		scanf("%d",&m);
		if(m==3) break;
		else if(m==1)
		{
			scanf("%d%d%d",&x,&y,&k);
			x++,y++;
			add(x,y,k);
		}
		else
		{
			scanf("%d%d%d%d",&x,&y,&xx,&yy);
			ans=work(xx+1,yy+1)-work(xx+1,y)-work(x,yy+1)+work(x,y);
			printf("%d\n",ans);
		}
	}
	return 0;
} 

[USACO17JAN]Balanced Photo平衡的照片

题目描述

FJ正在安排他的N头奶牛站成一排来拍照。(1<=N<=100,000)序列中的第i头奶牛的高度是h[i],且序列中所有的奶牛的身高都不同。

就像他的所有牛的照片一样,FJ希望这张照片看上去尽可能好。他认为,如果存在i满足max(Ri,Li)>2*min(Li,Ri),第i头奶牛就是不平衡的。(Li和Ri分别代表第i头奶牛左右两边比她高的数量)。

也就是说,如果Li和Ri中的较大者严格来说是这两个数字中较小者的两倍以上,那么i就是不平衡的。

FJ不希望他有太多的奶牛不平衡,请你帮助FJ计算不平衡的奶牛数量。

输入格式

第一行一个整数N。

接下N行包括H[1]到H[n],每行一个非负整数(不大于1,000,000,000)。

输出格式

一行,一个整数,表示不平衡的奶牛数量。

样例数据
input

7
34
6
23
0
5
99
2

output

3

数据规模与约定

时间限制:1s
空间限制:256MB

#include
using namespace std;
#define N 100010
int n,c[N],a[N],z[N],y[N],ans,len,b[N];
int lowbit(int x){return x&-x;}
int add(int x,int y){while(x<=n)c[x]+=y,x+=lowbit(x);}
int work(int x)
{
	int sum=0;
	while(x>0)sum+=c[x],x-=lowbit(x);
	return sum;
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++) {scanf("%d",&a[i]);b[i]=a[i];} 
	sort(b+1,b+n+1);
	len=unique(b+1,b+n+1)-(b+1);
	for(int i=1;i<=n;i++) a[i]=lower_bound(b+1,b+len+1,a[i])-b;
	for(int i=1;i<=n;i++) {z[i]=i-1-work(a[i]);add(a[i],1);}
	memset(c,0,sizeof(c));
	for(int i=n;i>=1;i--) {y[i]=n-i-work(a[i]);add(a[i],1);}
	for(int i=1;i<=n;i++)
		if(2*min(z[i],y[i])<max(z[i],y[i])) ans++;
	cout<<ans;
}

人品累加和

题目描述

人品是必不可少的,人品还是守恒的。每个人的人品都是不同的,并且有正的(选择题可以用骰子全过),也有负的。

jzyz有n (1 <= n <= 100,000)个学生,第i个人的人品值是A_i(-10,000 <= A_i <= 10,000).

jzyz决定重新规划寝室,要让每个寝室的人品和不小于0.寝室的人品和是这个寝室里所有人的人品和。

因为名单是按照学籍号给出的,所以,现在你不能调整学生的顺序,只能按照输入的顺序把学生依次分进各个寝室,当然,为了增加难度,每个寝室的人数可以不同,我们甚至可以把n个学生放在同一个寝室。我们的口号,人品比宿舍重要。

比如现在有4个人,人品分别是2, 3, -3, 1。我们可以按照下面分寝室:

(2 3 -3 1)
(2 3 -3) (1)
(2) (3 -3 1)
(2) (3 -3) (1)
这样保证每个寝室的人品和不小于0.

现在的问题是,如果按照这种原则,有多少种分寝室的方法。当然,方法可能很多,你只需要输出方案数对 1,000,000,009 取余数即可。

输入格式

第一行一个整数n

接下来n行,表示每个学生的人品A_i.

输出格式

一个整数,如题所求的那个余数

input

4
2
3
-3
1

output

4

数据规模与约定

40% n<=1000.
100% 数据如题目描述。
时间限制:1s
空间限制:256MB

#include
using namespace std;
#define int long long
#define MOD 1000000009
#define N 100010
int len,c[N],b[N],n,f[N],a[N],ans;
int lowbit(int x){return x&-x;}
void add(int x,int y){while(x<=n+1){f[x]+=y;f[x]%=MOD;x+=lowbit(x);}}
int ask(int x)
{
	int c=0;
	while(x){c=(c+f[x])%MOD;x-=lowbit(x);}
	return c%MOD;
}
signed main()
{
	scanf("%lld",&n);
	for(int i=1;i<=n;i++){scanf("%lld",&a[i]);c[i]=c[i-1]+a[i];b[i]=c[i];}
	sort(b+1,b+2+n);
	len=unique(b+1,b+2+n)-(b+1);
	for(int i=1;i<=1+n;i++){c[i]=lower_bound(b+1,b+n+2,c[i])-b;}
	add(c[n+1],1);
	for(int i=1;i<=n;i++){ans=ask(c[i])%MOD;add(c[i],ans);}
	printf("%lld",ans);
	return 0;
}

楼兰图腾

Description

在完成了分配任务之后,西部314来到了楼兰古城的西部。相传很久以前这片土地上(比楼兰古城还早)生活着两个部落,一个部落崇拜尖刀(‘V’),一个部落崇拜铁锹(‘∧’),他们分别用V和∧的形状来代表各自部落的图腾。

西部314在楼兰古城的下面发现了一幅巨大的壁画,壁画上被标记出了N个点,经测量发现这N个点的水平位置和竖直位置是两两不同的。西部314认为这幅壁画所包含的信息与这N个点的相对位置有关,因此不妨设坐标分别为(1,y1),(2,y2),…,(n,yn),其中y1~yn是1到n的一个排列。

西部314打算研究这幅壁画中包含着多少个图腾,其中V图腾的定义如下(注意:图腾的形式只和这三个纵坐标的相对大小排列顺序有关)1 <= i < j < k <= n且yi > yj,yj < yk;

而崇拜∧的部落的图腾被定义为1 <= i < j < k <= n且yi < yj , yj > yk;

西部314想知道,这n个点中两个部落图腾的数目。因此,你需要编写一个程序来求出V的个数和∧的个数。

= = = 精简版说明:

平面上有 N(N≤10^5 ) 个点,每个点的横、纵坐标的范围都是 1~N,任意两个点的横、纵坐标都不相同。

若三个点 ( x 1 , y 1 ) , ( x 2 , y 2 ) , ( x 3 , y 3 ) (x_1,y_1),(x_2,y_2),(x_3,y_3 ) (x1,y1),(x2,y2),(x3,y3) 满足 x 1 < x 2 < x 3 , y 1 > y 2 x_1 < x_2 < x_3, y_1 > y_2 x1<x2<x3,y1>y2 并且 y 3 > y 2 y_3 > y_2 y3>y2,则称这三个点构成"v"字图腾。

若三个点 ( x 1 , y 1 ) , ( x 2 , y 2 ) , ( x 3 , y 3 ) (x_1,y_1),(x_2,y_2),(x_3,y_3 ) (x1,y1),(x2,y2),(x3,y3) 满足 x 1 < x 2 < x 3 , y 1 < y 2 x_1 < x_2 < x_3, y_1 < y_2 x1<x2<x3,y1<y2 并且 y 3 < y 2 y_3 < y_2 y3<y2,则称这三个点构成"^"字图腾。

求平面上"v"和"^"字图腾的个数。
####Input Format

第一行一个数n

第二行是n个数,分别代表 y 1 , y 2 … … y n y_1,y_2……y_n y1y2yn

Output Format

两个数

中间用空格隔开

依次为V的个数和∧的个数

Sample Input

5
1 5 3 2 4

Sample Output

3 4

Hint

数据范围约定

10%的数据 n<=600

40%的数据 n<=5000

100%的数据 n<=200000,答案不超过int64
####Limitation

各个测试点1s

#include
using namespace std;
#define int long long
#define N 200010
int n,ans,a[N],r[N],l[N],c[N];
int lowbit(int x) {return x&-x;}
int ask(int x)
{
	int sum=0;
	while(x>0) sum+=c[x],x-=lowbit(x);
	return sum;
}
void add(int x,int y) {while(x<=n) c[x]+=y,x+=lowbit(x);}
signed main()
{
	scanf("%lld",&n);
	for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
	memset(c,0,sizeof(c));
	for(int i=n;i;i--) {r[i]=ask(a[i]-1);add(a[i],1);}
	memset(c,0,sizeof(c));
	for(int i=1;i<=n;i++) {l[i]=ask(a[i]-1);add(a[i],1);}
	for(int i=1;i<=n;i++) ans+=(i-1-l[i])*(n-i-r[i]);  
	printf("%lld ",ans);
	ans=0;
	for(int i=1;i<=n;i++) ans+=(l[i])*r[i];
	printf("%lld",ans);
} 

A Simple Problem with Integers

题目描述

对于数列 A 1 A_1 A1, A 2 A_2 A2, … , A N A_N AN. 你要进行2个操作:将一个区间的数同加上某个数,输出一段区间的和。

输入格式

第一行2个整数表示数列长度和操作次数. 1 ≤ N,Q ≤ 100000.

第二行为数列 A1, A2, … , AN. -1000000000 ≤ Ai ≤ 1000000000.

接下来的Q行操作:

“C a b c” 表示将 Aa, Aa+1, … , Ab.加上c. -10000 ≤ c ≤ 10000.

“Q a b” 输出区间[a,b]的和。

输出格式

输出所有询问的答案,每行1个。

样例数据
input

10 5
1 2 3 4 5 6 7 8 9 10
Q 4 4
Q 1 10
Q 2 4
C 3 6 3
Q 2 4

output

4
55
9
15

数据规模与约定

时间限制:5s
空间限制:128MB

#include
using namespace std;
#define int long long
#define N 100010
int n,m,sum[N],a[N],c[2][N],ans,l,r,d;
int lowbit(int x){return x&-x;}
void add(int x,int y,int z){for(;y<=n;y+=lowbit(y))c[x][y]+=z;}
int ask(int x,int y){int ans=0;for(;y;y-=lowbit(y))ans+=c[x][y];return ans;}
signed main()
{
	scanf("%lld%lld",&n,&m);
	for(int i=1;i<=n;i++){scanf("%lld",&a[i]);sum[i]=sum[i-1]+a[i];}
	while(m--)
	{char op[2];
		scanf("%s%lld%lld",op,&l,&r);
		if(op[0]=='C')
		{
			scanf("%lld",&d);
			add(0,l,d);add(0,r+1,-d);add(1,l,l*d);add(1,r+1,-(r+1)*d);
		}
		else
		{
			ans=sum[r]+(r+1)*ask(0,r)-ask(1,r);
			ans-=sum[l-1]+l*ask(0,l-1)-ask(1,l-1);
			cout<<ans<<endl;
		}
	}
	return 0;
}

[Poi2012]Letters

题目描述

给出两个长度相同且由大写英文字母组成的字符串A、B,保证A和B中每种字母出现的次数相同。

现在每次可以交换A中相邻两个字符,求最少需要交换多少次可以使得A变成B。

输入格式

第一行一个正整数n (2<=n<=1,000,000),表示字符串的长度。

第二行和第三行各一个长度为n的字符串,并且只包含大写英文字母。

输出格式

一行,一个整数,表示答案。

样例数据
input

3
ABC
BCA

output

2

样例解释:ABC -> BAC -> BCA
数据规模与约定

时间限制:1s
空间限制:256MB

#include 
using namespace std;
#define int long long
#define N 1000010
char A[N],B[N];
int sum[N],a[N],n,ans;
vector<int> f[30];
int lowbit(int x){return x&-x;}
void add(int x)
{
	for(;x<=n;x+=lowbit(x)) sum[x]++;
}
long long get(int x)
{
	long long ans=0;
	for(;x;x-=lowbit(x)) ans+=sum[x];
	return ans;
}
signed main()
{
	scanf("%lld%s%s",&n,A+1,B+1);
	for(int i=1;i<=n;i++) f[(int)A[i]-64].push_back(i);
	for(int i=n;i>=1;i--)
	{
		a[i]=f[(int)B[i]-64].back();
		f[(int)B[i]-64].pop_back();
	}
	ans=0;
	for(int i=1;i<=n;i++){add(a[i]);ans+=i-get(a[i]);}
	cout<<ans;
	return 0;
}

小x的新年

题目描述

马上就是新年了,小x想要带自己喜欢的女孩子去放孔明灯,但他发现,孔明灯被锁在了抽屉里。而抽屉是有魔法的抽屉,只有小x完成了它的要求它才能打开。

抽屉的要求如下:小x将被给予一个长度为 N N N 的整数序列 A A A

对于该序列,抽屉将会对小x提出 Q Q Q 次要求,对于第 i i i 个要求,抽屉将会给小x三个魔法数字: T i T_i Ti X i X_i Xi Y i Y_i Yi,而这三个数字的含义如下:

如果 T i T_i Ti为1,那么小x需要将 A X A_X AX替换为 A X A_X AX ⨁ \bigoplus Y i Y_i Yi

如果 T i T_i Ti为2,那么小x需要输出 A X A_X AX ⨁ \bigoplus A X + 1 A_{X+1} AX+1 ⨁ \bigoplus A X + 2 A_{X+2} AX+2 ⨁ \bigoplus ⨁ \bigoplus A Y A_Y AY

请你帮助小x一起解决这个问题,因为他急着去放孔明灯(滑稽)。

题目翻译来自2023届某位不知名选手lyn

输入格式

第一行一个N 一个Q;

第二行给出长度为n的整数序列 A 1 A_1 A1 A 2 A_2 A2 A 3 A_3 A3 A N A_N AN

接下来Q行 每行如 T 1 T_1 T1 X 1 X_1 X1 Y 1 Y_1 Y1

T Q T_Q TQ X Q X_Q XQ Y Q Y_Q YQ

输出格式

每行一个 对于 T T T=2时的答案。

样例数据

input

3 4
1 2 3
2 1 3
2 2 3
1 2 3
2 2 3

output

0
1
2

input

10 10
0 5 3 4 7 0 0 0 1 0
1 10 7
2 8 9
2 3 6
2 1 6
2 1 10
1 9 4
1 6 1
1 6 3
1 1 7
2 3 5

output

1
0
5
3
0

数据规模与约定

  • 1≤ N N N≤300000
  • 1≤ Q Q Q≤300000
  • 0≤ A i A_i Ai< 2 30 2^{30} 230
  • T i T_i Ti 是 1或 2.
  • 如果 T i T_i Ti=1, 那么 1 ≤ X i X_i Xi N N N 并且 0 ≤ Y i Y_i Yi< 2 30 2^{30} 230.
  • 如果 T i T_i Ti=2 那么 1≤ X i X_i Xi Y i Y_i Yi N N N.
  • 保证所有的数都是整数.

atcoder abc 185F

时间限制: 1 s 1 \text {s} 1s

空间限制: 256 MB 256 \text {MB} 256MB

#include
using namespace std;
#define N 10001000
#define int long long
int n,a[N],c[N],m,l,r,ch;
int lowbit(int x){return x&-x;}
void add(int x,int y){while(x<=n)c[x]^=y,x+=lowbit(x);}
int work(int x)
{
	int sum=0;
	while(x>0) sum^=c[x],x-=lowbit(x);
	return sum;
}
signed main()
{
	scanf("%lld%lld",&n,&m);
	for(int i=1;i<=n;i++){scanf("%lld",&a[i]);add(i,a[i]);}
	for(int i=1;i<=m;i++)
	{
		scanf("%lld%lld%lld",&ch,&l,&r);
		if(ch==1) add(l,r);
		else printf("%lld\n",work(l-1)^work(r));
	}
	return 0;
}

[倍增]忠诚

题目描述

老管家是一个聪明能干的人。他为财主工作了整整10年,财主为了让自已账目更加清楚。

要求管家每天记k次账,由于管家聪明能干,因而管家总是让财主十分满意。但是由于一些人的挑拨,财主还是对管家产生了怀疑。

于是他决定用一种特别的方法来判断管家的忠诚,他把每次的账目按1,2,3…编号,然后不定时的问管家问题,

问题是这样的:在a到b号账中最少的一笔是多少?为了让管家没时间作假他总是一次问多个问题。

输入格式

输入中第一行有两个数m,n表示有m(m<=100000)笔账,n表示有n个问题,n<=100000。

第二行为m个数,分别是账目的钱数

后面n行分别是n个问题,每行有2个数字说明开始结束的账目编号。

输出格式

输出文件中为每个问题的答案。具体查看样例。

样例数据
input

10 3
1 2 3 4 5 6 7 8 9 10
2 7
3 9
1 10

output

2 3 1
数据规模与约定
时间限制:1s
空间限制:256MB

#include
using namespace std;
#define N 100010
int m,n,ans,a[N][50],t[N],l,r;
int main()
{
	t[0]=1;for(int i=1;i<=17;i++)t[i]=2*t[i-1];
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++) scanf("%d",&a[i][0]);
	for(int j=1;j<=17;j++)
		for(int i=1;i+t[j-1]<=n;i++)
			a[i][j]=min(a[i][j-1],a[i+t[j-1]][j-1]);
	while(m--)
	{
		scanf("%d%d",&l,&r);
		ans=log(r-l+1)/log(2);ans=min(a[l][ans],a[r-t[ans]+1][ans]);
		printf("%d ",ans);
	}
	return 0;
}

LCA模板

题目描述

给定n个点的树(1是根),m次询问,每次询问两点的LCA;

输入格式

第一行两个整数,n,m;

接下来n-1行,给定正整数a,b,表示a,b间有边;

接下来m行,给定整数a,b,表示询问a,b;

输出格式

对于每个询问,输出它们的LCA;

样例数据

input

3 2
1 2
2 3
1 2
2 3

output

1
2

数据规模与约定

保证所有数据 n , m < = 100000 n,m<=100000 nm<=100000

(事实上,除了第8组数据以外,另外九组n=m=100000……)

时间限制: 1 s 1 \text {s} 1s

空间限制: 256 MB 256 \text {MB} 256MB

#include 
using namespace std;
#define N 1000100
int n,m,a,b;
struct shu{
	int tot,f[N][30],c[N<<1],die[N<<1],tou[N<<1],d[N];
	void add(int x,int y) {c[++tot]=y;die[tot]=tou[x];tou[x]=tot;}
	void chushi()
	{
		f[1][0]=0;
		for(int j=1;(1<<j)<n;j++)
			for(int i=1;i<=n;i++)
			{
				if(f[i][j-1]==0) f[i][j]=0;
				else f[i][j]=f[f[i][j-1]][j-1];
			}
	}
	void dfs(int x)
	{
		d[x]=d[f[x][0]]+1 ;
		for(int i=tou[x];i;i=die[i])
		{
			int y=c[i] ;
			if (y==f[x][0]) continue ;
			f[y][0]=x;
			dfs(y);
		}
	}
	int ask(int x,int y)
	{
		if(d[x]>d[y]) swap(x,y);
		tot=log(n)/log(2)+1;
		for(int i=tot;i>=0;i--)
			if(d[f[y][i]]>=d[x]) y=f[y][i];
		if(x==y) return x;
		for(int i=tot;i>=0;i--)
			if(f[x][i]!=f[y][i]) {x=f[x][i];y=f[y][i];}
		return f[x][0];
	}
}tr;
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<n;i++) {scanf("%d%d",&a,&b);tr.add(a,b);tr.add(b,a);}
	tr.dfs(1);
	tr.chushi();
	while(m--) {scanf("%d%d",&a,&b);printf("%d\n",tr.ask(a,b));}
	return 0;
}

树上任意两点距离

题目描述

给出 n n n个点的一棵树,多次询问两点之间的最短距离。

输入格式

第一行为两个整数 n n n m m m

n n n 表示点数, m m m表示询问次数;

接下来 n − 1 n-1 n1行,每行三个整数 x , y , k x,y,k x,y,k ,表示点 x x x和点 y y y之间存在一条边长度为 k k k

再接下来 m m m行,每行两个整数 x , y x,y x,y ,表示询问点 x x x到点 y y y 的最短距离。

输出格式

输出 m m m行。对于每次询问,输出一行一个整数表示答案。

样例数据

input

2 2 
1 2 100 
1 2 
2 1

output

100
100

input

3 2
1 2 10
3 1 15
1 2
3 2

output

10
25

数据规模与约定

100 100% 100 数据保证, 2 ≤ n , m , k ≤ 100000 2 \leq n,m,k \leq 100000 2n,m,k100000

时间限制: 1 s 1 \text {s} 1s

空间限制: 256 MB 256 \text {MB} 256MB

#include
using namespace std;
#define N 100010
#define int long long
int n,m,a,b,f[N<<1][100],maxx,d[N],tot,head[N],c,dis[N];
bool flag[N];
struct hhhc{
	int y,next,v;
}o[N<<1];
void chuli()
{
	for(int j=1;j<=maxx;j++)
		for(int i=1;i<=n;i++)
			f[i][j]=f[f[i][j-1]][j-1];
}
void add(int a,int b,int c){tot++;o[tot].y=b;o[tot].next=head[a];o[tot].v=c;head[a]=tot;}
void dfs(int x)
{ 
	for(int i=head[x];i;i=o[i].next)
	{
		if(flag[o[i].y]) continue;
		dis[o[i].y]=dis[x]+o[i].v;
		d[o[i].y]=d[x]+1;
		flag[o[i].y]=1;
		f[o[i].y][0]=x;
		dfs(o[i].y);
	}
} 
int work(int x,int y)
{
	if(d[x]<d[y]) swap(x,y);
	for(int i=maxx;i>=0;i--) if(d[f[x][i]]>=d[y]) x=f[x][i];
	if(x==y) return x;
	for(int i=maxx;i>=0;i--)
		if(f[x][i]!=f[y][i]) {x=f[x][i];y=f[y][i];} 
	return f[x][0];
}
signed main()
{
	scanf("%lld%lld",&n,&m);
	maxx=log(n)/log(2)+1;
	for(int i=1;i<n;i++){scanf("%lld%lld%lld",&a,&b,&c);add(a,b,c);add(b,a,c);}
	d[1]=1;flag[1]=1;
	dfs(1);chuli();
	while(m--){scanf("%lld%lld",&a,&b);printf("%lld\n",dis[a]+dis[b]-2*dis[work(a,b)]);}
	return 0;
}

【bzoj 4390】 [Usaco2015 dec]Max Flow

题目描述

Farmer John has installed a new system of N-1 pipes to transport milk between the N stalls in his barn (2≤N≤50,0002 \leq N \leq 50,0002≤N≤50,000), conveniently numbered 1 … N 1 \ldots N 1N. Each pipe connects a pair of stalls, and all stalls are connected to each-other via paths of pipes.

FJ is pumping milk between KKK pairs of stalls (1≤K≤100,0001 \leq K \leq 100,0001≤K≤100,000). For the iiith such pair, you are told two stalls sis_isi and tit_iti, endpoints of a path along which milk is being pumped at a unit rate. FJ is concerned that some stalls might end up overwhelmed with all the milk being pumped through them, since a stall can serve as a waypoint along many of the KKK paths along which milk is being pumped. Please help him determine the maximum amount of milk being pumped through any stall. If milk is being pumped along a path from sis_isi to tit_iti, then it counts as being pumped through the endpoint stalls sis_isi and tit_iti, as well as through every stall along the path between them.

FJ给他的牛棚的N(2≤N≤50,000)个隔间之间安装了N-1根管道,隔间编号从1到N。所有隔间都被管道连通了。

FJ有K(1≤K≤100,000)条运输牛奶的路线,第i条路线从隔间si运输到隔间ti。

一条运输路线会给它的两个端点处的隔间以及中间途径的所有隔间带来一个单位的运输压力,你需要计算压力最大的隔间的压力是多少。

输入格式

The first line of the input contains N and K.

The next N?1 lines each contain two integers x and y (x≠y) describing a pipe between stalls x and y.

The next K lines each contain two integers s and t describing the endpoint stalls of a path through which milk is being pumped.

输出格式

An integer specifying the maximum amount of milk pumped through any stall in the barn.

样例数据

input

5 10
3 4
1 5
4 2
5 4
5 4
5 4
3 5
4 3
4 3
1 3
3 5
5 4
1 5
3 4

output

9

数据规模与约定

时间限制: 1 s 1 \text {s} 1s

空间限制: 256 MB 256 \text {MB} 256MB

#include 
using namespace std;
#define N 100010
int n,m,f[N][30],v[N<<1],die[N<<1],head[N<<1],d[N],t,a,b,s[N<<1],ans;
void add(int x,int y) {v[++t]=y;die[t]=head[x];head[x]=t;}
void bfs(int x)
{
	d[x]=d[f[x][0]]+1 ;
	for(int i=head[x];i;i=die[i])
	{
		int y=v[i] ;
		if(y==f[x][0]) continue ;
		f[y][0]=x;
		bfs(y);
	}
}
void work(int x,int fa)
{
	for(int i=head[x];i;i=die[i])
	{
		int xx=v[i];
		if(xx==fa) continue;
		work(xx,x);
		s[x]+=s[xx];
	}
	ans=max(ans,s[x]);
}

int ask(int x,int y)
{
	if(d[x]>d[y]) swap(x,y);
	t=log(n)/log(2)+1;
	for(int i=t;i>=0;i--)
		if(d[f[y][i]]>=d[x]) y=f[y][i];
	if(x==y) return x;
	for(int i=t;i>=0;i--)
		if(f[x][i]!=f[y][i]) {x=f[x][i];y=f[y][i];}
	return f[x][0];
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<n;i++){scanf("%d%d",&a,&b);add(a,b);add(b,a);}
	bfs(1);
	for(int j=1;(1<<j)<n;j++)
		for(int i=1;i<=n;i++)
			if(f[i][j-1]==0) f[i][j]=0;
			else f[i][j]=f[f[i][j-1]][j-1];
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d",&a,&b);
		s[a]++;s[b]++;
		int xx=ask(a,b);
		s[xx]--;s[f[xx][0]]--;
	}
	work(1,0);
	printf("%d",ans);
	return 0;
}

【线段树模板题】序列操作1

题目描述

给定一个包含n个数的序列,初值全为0,现对这个序列有两种操作:

操作1:把 给定 第k1 个数改为k2;

操作2:查询 从第k1个数到第k2个数的最大值。(k1<=k2<=n)

所有的数都 <=100000

输入格式

第一行给定一个整数n,表示有n个操作。

以下接着n行,每行三个整数,表示一个操作。

第一个树表示操作序号,第二个数为k1,第三个数为k2

输出格式

若干行,查询一次,输出一次。

样例数据

input

3
1 2 2
1 3 3
2 2 3

output

3

数据规模与约定

保证$a,b \leq 10^5, n\leq 10^5 $。

时间限制: 1 s 1 \text {s} 1s

空间限制: 256 MB 256 \text {MB} 256MB

#include
using namespace std;
#define N 100010
int a[N],d,b,c,n;
struct ST{int r,l,dat;}t[N*4];
void build(int p,int l,int r)
{
	t[p].l=l;t[p].r=r;
	if(l==r){return;}
	int mid=(l+r)/2;
	build(p*2,l,mid);build(p*2+1,mid+1,r);
	t[p].dat=max(t[p*2].dat,t[p*2+1].dat);
}
void add(int p,int x,int v)
{
	if(t[p].l==t[p].r){t[p].dat=v;return;}
	int mid=(t[p].l+t[p].r)/2;
	if(x<=mid) add(p*2,x,v);
	else add(p*2+1,x,v);
	t[p].dat=max(t[p*2].dat,t[p*2+1].dat);
}
int ask(int p,int l,int r)
{
	if(l<=t[p].l&&r>=t[p].r) return t[p].dat;
	int mid=(t[p].l+t[p].r)/2;
	int val=-(1<<30);
	if(l<=mid) val=max(val,ask(p*2,l,r));
	if(r>mid) val=max(val,ask(p*2+1,l,r));
	return val;
}
int main()
{
	scanf("%d",&n);
	build(1,1,n);
	for(int i=1;i<=n;i++)
	{
		scanf("%d%d%d",&d,&b,&c);
		if(d==1) add(1,b,c);
		else printf("%d\n",ask(1,b,c)); 
	}
}

Can you answer on these queries III

题目描述

给定长度为N的数列A,以及M条指令 (N$\leq 500000 , M 500000, M 500000,M\leq$100000),每条指令可能是以下两种之一:

“2 x y”,把 A[x] 改成 y。

“1 x y”,查询区间 [x,y] 中的最大连续子段和,即$ \underset{x\leq l \leq r \leq y}{\operatorname{max}},{ \sum_{i=l}^{r} { A[i] }}$。
对于每个询问,输出一个整数表示答案。

输入格式

第一行两个整数N,M

第二行N个整数Ai

接下来M行每行3个整数k,x,y,k=1表示查询(此时如果x>y,请交换x,y),k=2表示修改

输出格式

对于每个询问输出一个整数表示答案。

样例数据

input

5 3
1 2 -3 4 5
1 2 3
2 2 -1
1 3 2

output

2
-1

数据规模与约定

对于100%的数据: N$\leq 500000 , M 500000, M 500000,M\leq 100000 , ∣ A i ∣ 100000, |Ai| 100000,Ai\leq$1000

时间限制: 10 s 10 \text {s} 10s

空间限制: 256 MB 256 \text {MB} 256MB

#include 
using namespace std;
#define N 500010
int n,m,flag,x,y,a[N];
struct ST{int l,r,lmax,rmax,sum,dat;}t[4*N];
void qiuhe(int p)
{
	t[p].sum=t[p*2].sum+t[p*2+1].sum;
	t[p].lmax=max(t[p*2].lmax,t[p*2].sum+t[p*2+1].lmax);
	t[p].rmax=max(t[p*2+1].rmax,t[p*2+1].sum+t[p*2].rmax);
	t[p].dat=max(t[p*2].dat,max(t[p*2+1].dat,t[p*2].rmax+t[p*2+1].lmax));
}
void build(int p,int l,int r)
{
	t[p].l=l;
	t[p].r=r;
	if(l==r)
	{
		t[p].dat=a[l];t[p].sum=a[l];t[p].rmax=a[r];t[p].lmax=a[l];
		return ;
	}
	int mid=(l+r)/2;
	build(p*2,l,mid);build(p*2+1,mid+1,r);
	qiuhe(p);
}
void change(int p,int x,int v)
{
	if(t[p].l==t[p].r)
	{
		t[p].dat=v;t[p].sum=v;t[p].lmax=v;t[p].rmax=v;
		return ;
	}
	int mid=(t[p].l+t[p].r)/2;
	if(x<=mid) change(p*2,x,v);
	else change(p*2+1,x,v);
	qiuhe(p);
}

ST get(int p,int l,int r)
{
    if(l<=t[p].l&&r>=t[p].r) return t[p];
    int mid=(t[p].l+t[p].r)/2;
    if(mid>=r) return get(p*2,l,r);
    if(mid<l) return get(p*2+1,l,r);
    else
	{
        ST ans,left,right;
        left=get(p*2,l,r);
        right=get(p*2+1,l,r);
        ans.sum=left.sum+right.sum;
        ans.dat=max(max(left.dat,left.rmax+right.lmax),right.dat);
        ans.lmax=max(left.lmax,left.sum+right.lmax);
        ans.rmax=max(right.rmax,right.sum+left.rmax);
        return ans;
    }
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	build(1,1,n);
	while(m--)
	{
		scanf("%d%d%d",&flag,&x,&y);
		if(flag==2) change(1,x,y);
		else {if(x>y) swap(x,y);printf("%d\n",get(1,x,y).dat);}
	}
	return 0;
}

【线段树模板题】序列操作2

题目描述

给定一个包含n个数的序列,初值全为0,现对这个序列有两种操作:

操作1:将第k1 个数 到 第k2 个数加1;

操作2:查询 从第k1个数到第k2个数的最大值。(k1<=k2<=n)

所有的数都 <=100000

输入格式

第一行给定一个整数n,表示有n个操作。

以下接着n行,每行三个整数,表示一个操作。

输出格式

若干行,查询一次,输出一次。

样例数据

input

3
1 2 2
1 3 3
2 2 3

output

1

数据规模与约定

保证$a,b \leq 10^9, n\leq 10^5 $。

时间限制: 1 s 1 \text {s} 1s

空间限制: 256 MB 256 \text {MB} 256MB

#include
using namespace std;
#define ll long long
struct node
{
	int l,r;
	ll sum,add;
	#define l(x) tree[x].l
	#define r(x) tree[x].r
	#define sum(x) tree[x].sum
	#define add(x) tree[x].add
}tree[100010*4];
int n,a[100100],x,y,z;
void build(int p,int l,int r)
{
	l(p)=l,r(p)=r;
	if(l==r) { sum(p)=a[l]; return;	}
	int mid=(l+r)/2;
	build(p*2,l,mid);build(p*2+1,mid+1,r);
	sum(p)=max(sum(p*2),sum(p*2+1));
}
void spread(int p)
{
	if(add(p))
	{
		sum(p*2)=sum(p*2)+add(p);
		sum(p*2+1)+=add(p);
		add(p*2)+=add(p);
		add(p*2+1)+=add(p);
		add(p)=0;
	}
}
void charge(int p,int l,int r,int d)
{
	if(l<=l(p)&&r>=r(p))
	{
		sum(p)+=d;
		add(p)+=d;
		return;
	}
	spread(p);
	int mid=(r(p)+l(p))/2;
	if(l<=mid) charge(p*2,l,r,d);
	if(r>mid) charge(p*2+1,l,r,d);
	sum(p)=max(sum(p*2),sum(p*2+1));
}
ll ask(int p,int l,int r)
{
	if(l<=l(p)&&r>=r(p)) return sum(p);
	spread(p);
	int mid=(l(p)+r(p))/2;
	ll val=0;
	if(l<=mid) val=max(val,ask(p*2,l,r));
	if(r>mid) val=max(val,ask(p*2+1,l,r));
	return val;
}
int main()
{
	scanf("%d",&n);
	build(1,1,n);
	while(n--)
	{
		scanf("%d%d%d",&x,&y,&z);
		if(x==1) charge(1,y,z,1);
		else printf("%lld\n",ask(1,y,z));
	}
	return 0;
}

农场分配

题目描述

Farmer John最近新建立了一个农场,并且正在接受奶牛的畜栏分配请求,有些
畜栏会看到农场美妙的风景。

农场由N (1 <= N <= 100,000) 个畜栏组成,编号为1…N,畜栏i可以最多容纳C_i只奶牛
(1 <= C_i <= 100,000)。奶牛i希望得到连续的一段畜栏,表示为一段区间 (A_i,B_i) 。
这样的话奶牛可以在这段牛棚里面转悠。(当然,这段畜栏必须要有足够的空间)

给出M (1 <= M <= 100,000) 个请求,请求出不超过畜栏承载量的情况下,最多可以满足的请求数。

考虑下面展示的一个农场:

编号 1 2 3 4 5

容量 | 1 | 3 | 2 | 1 | 3 |

奶牛1 (1, 3)

奶牛2 (2, 5)

奶牛3 (2, 3)

奶牛4 (4, 5)

FJ 不能够同时满足4头奶牛的请求,否则3,4号畜栏就会有太多的奶牛。

考虑到奶牛2的请求需要一个区间包含3号和4号畜栏,我们尝试这样一种方案,让1,3,4号奶牛
的请求都得到满足,这样没有畜栏超出容量的限制,因此,对于上述情况的答案就是3,三头奶牛
(1,3,4号)的要求可以得到满足。

输入格式

  • 第1行:两个用空格隔开的整数:N和M

  • 第2行到N+1行:第i+1行表示一个整数C_i

  • 第N+2到N+M+1行: 第i+N+1行表示2个整数 A_i和B_i

输出格式

  • 第一行: 一个整数表示最多能够被满足的要求数

样例数据

input

5 4
1
3
2
1
3
1 3
2 5
2 3
4 5

output

3

数据规模与约定

如题目描述

时间限制: 1 s 1 \text {s} 1s

空间限制: 256 MB 256 \text {MB} 256MB

#include 
using namespace std;
#define N 100010
int ans,n,m,p,a[N],pd,k1,k2;
struct node
{
	int l,r;
	long long dat,add;
	#define l(x) tree[x].l
	#define r(x) tree[x].r
	#define dat(x) tree[x].dat
	#define add(x) tree[x].add
}tree[N*4];
struct node1{int l,r;}f[N];
bool mycmp(node1 x,node1 y){return x.r<y.r;}
void build(int p,int l,int r)
{
	l(p)=l;r(p)=r;
	if(l==r) {dat(p)=a[l];return;}
	int mid=((l+r)>>1);
	build(p*2,l,mid);build(p*2+1,mid+1,r);
	dat(p)=min(dat(p*2),dat(p*2+1));
}
void spread(int p)
{
	if(add(p))
	{
		dat(p*2)-=add(p);
		dat(p*2+1)-=add(p);
		add(p*2)+=add(p);
		add(p*2+1)+=add(p);
		add(p)=0;
	}
}
void change(int p,int l,int r,int v)
{
	if(l<=l(p)&&r>=r(p)) {dat(p)-=v;add(p)+=v;return;}
	spread(p);
	int mid=(l(p)+r(p))>>1;
	if(l<=mid) change(p*2,l,r,v);
	if(r>mid) change(p*2+1,l,r,v);
	dat(p)=min(dat(p*2),dat(p*2+1));
}
int get(int p,int l,int r)
{
	if(l<=l(p)&&r>=r(p)) return dat(p);
	spread(p);
	int mid=(l(p)+r(p))>>1,val=9999999;
	if(l<=mid) val=min(val,get(p*2,l,r));
	if(r>mid) val=min(val,get(p*2+1,l,r));
	return val;
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++){scanf("%d",&a[i]);}
	for(int i=1;i<=m;i++){scanf("%d%d",&f[i].l,&f[i].r);}
	sort(f+1,f+m+1,mycmp);build(1,1,f[m].r);
	for(int i=1;i<=m;i++)
	{
		p=get(1,f[i].l,f[i].r);
		if(p>0)
		{
			change(1,f[i].l,f[i].r,1);
			ans++;
		}
	}
	cout<<ans;
	return 0;
}

【usaco月赛】hotel

题目描述

奶牛们最近的旅游计划,是到苏必利尔湖畔,享受那里的湖光山色,以及明媚的阳光。作为整个旅游的策划者和负责人,贝茜选择在湖边的一家著名的旅馆住宿。

这个巨大的旅馆一共有 N ( 1 < = N < = 50000 ) N (1 <= N <= 50000) N(1<=N<=50000)间客房,它们在同一层楼中顺次一字排开,在任何一个房间里,只需要拉开窗帘,就能见到波光粼粼的湖面。

贝茜一行,以及其他慕名而来的旅游者,都是一批批地来到旅馆的服务台,希望能订到 D i ( 1 < = D i < = N ) D_i (1 <= D_i <= N) Di(1<=Di<=N)间连续的房间。

服务台的接待工作也很简单:如果存在 r r r满足编号为 r . . r + D i − 1 r..r+D_i-1 r..r+Di1的房间均空着,他就将这一批顾客安排到这些房间入住;如果没有满足条件的 r r r,他会道歉说没有足够的空房间,请顾客们另找一家宾馆。如果有多个满足条件的 r r r,服务员会选择其中最小的一个。

旅馆中的退房服务也是批量进行的。每一个退房请求由2个数字 X i X_i Xi D i D_i Di描述,表示编号为 X i . . X i + D i − 1 ( 1 < = X i < = N − D i + 1 ) X_i..X_i+D_i-1 (1 <= X_i <= N-D_i+1) Xi..Xi+Di1(1<=Xi<=NDi+1)房间中的客人全部离开。退房前,请求退掉的房间中的一些,甚至是所有,可能本来就无人入住。

而你的工作,就是写一个程序,帮服务员为旅客安排房间。你的程序一共需要处理 M ( 1 < = M < 50000 ) M (1 <= M < 50000) M(1<=M<50000)个按输入次序到来的住店或退房的请求。第一个请求到来前,旅店中所有房间都是空闲的。

输入格式

第1行: 2个用空格隔开的整数: N N N M M M

第2…M+1行: 第 i + 1 i+1 i+1描述了第 i i i个请求,如果它是一个订房请求,则用2个数字 1 、 D i 1、D_i 1Di描述,数字间用空格隔开;如果它是一个退房请求,用3个以空格隔开的数字 2 、 X i 、 D i 2、X_i、D_i 2XiDi描述

输出格式

第1…行: 对于每个订房请求,输出1个独占1行的数字:如果请求能被满足,输出满足条件的最小的 r r r;如果请求无法被满足,输出0。

样例数据

input

10 6
1 3
1 3
1 3
1 3
2 5 5
1 6

output

1
4
7
0
5

数据规模与约定

时间限制: 3 s 3 \text {s} 3s

空间限制: 64 MB 64 \text {MB} 64MB

#include
using namespace std;
#define N 100010
int n,m,x,y,k,flag;
struct xds
{
	int z[N<<2],y[N<<2],rsum[N<<2],lsum[N<<2],sum[N<<2],dat[N<<2];
	void build(int p,int l,int r)
	{
		z[p]=l,y[p]=r;
		sum[p]=lsum[p]=rsum[p]=r-l+1;
		if(l==r) return ;
		int mid=(l+r)>>1;
		build(p<<1,l,mid);
		build(p<<1|1,mid+1,r);
	}
	void spread(int p)
	{
		if(dat[p])
		{
			dat[p<<1]=dat[p<<1|1]=dat[p];
			if(dat[p]==1)
			{
				sum[p<<1]=lsum[p<<1]=rsum[p<<1]=0;
				sum[p<<1|1]=lsum[p<<1|1]=rsum[p<<1|1]=0;
			}
			else
			{
				sum[p<<1]=lsum[p<<1]=rsum[p<<1]=y[p<<1]-z[p<<1]+1;
				sum[p<<1|1]=lsum[p<<1|1]=rsum[p<<1|1]=y[p<<1|1]-z[p<<1|1]+1;
			}
			dat[p]=0;
		}
	}
	void add(int p,int l,int r,int v)
	{
		if(z[p]>=l&&y[p]<=r)
		{ 
		 	dat[p]=v;
			if(dat[p]==1) sum[p]=lsum[p]=rsum[p]=0;
			else sum[p]=lsum[p]=rsum[p]=y[p]-z[p]+1;
		 	return ;
		}
		spread(p);
		int mid=(z[p]+y[p])>>1;
		if(l<=mid) add(p<<1,l,r,v);
		if(r>mid) add(p<<1|1,l,r,v);
		lsum[p]=lsum[p<<1]==(y[p<<1]-z[p<<1]+1)?lsum[p<<1]+lsum[p<<1|1]:lsum[p<<1];
		rsum[p]=rsum[p<<1|1]==(y[p<<1|1]-z[p<<1|1]+1)?rsum[p<<1|1]+rsum[p<<1]:rsum[p<<1|1];
		sum[p]=max(max(sum[p<<1],sum[p<<1|1]),rsum[p<<1]+lsum[p<<1|1]);
	}
	int ask(int p,int v)
	{
		if(z[p]==y[p]) return z[p];
		spread(p);
		if(sum[p<<1]>=v) return ask(p<<1,v);
		if(rsum[p<<1]+lsum[p<<1|1]>=v) return y[p<<1]-rsum[p<<1]+1;
		return ask(p<<1|1,v);
	}
}tr;
int main()
{
	scanf("%d%d",&n,&m);
	tr.build(1,1,n);
	while(m--)
	{
		scanf("%d",&flag);
		if(flag==1)
		{
			scanf("%d",&x);
			k=tr.ask(1,x);
			if(k+x-1>n) puts("0");
			else {printf("%d\n",k);tr.add(1,k,k+x-1,1);}
		}
		else{scanf("%d%d",&x,&y);tr.add(1,x,x+y-1,-1);}
	}
	return 0;
}

【ahoi2009】维护序列-区间乘与区间加

题目描述

老师交给小可可一个维护数列的任务,现在小可可希望你来帮他完成。

有长为N的数列,不妨设为a1,a2,…,aN 。

有如下三种操作形式:

(1)把数列中的一段数全部乘一个值;

(2)把数列中的一段数全部加一个值;

(3)询问数列中的一段数的和,由于答案可能很大,你只需输出这个数模P的值。

输入格式

第一行两个整数N和P(1≤P≤1000000000)。

第二行含有N个非负整数,从左到右依次为a1,a2,…,aN, (0≤ai≤1000000000,1≤i≤N)。

第三行有一个整数M,表示操作总数。从第四行开始每行描述一个操作,输入的操作有以下三种形式:

操作1:“1 t g c”(不含双引号)。表示把所有满足t≤i≤g的ai改为ai×c (1≤t≤g≤N,0≤c≤1000000000)。

操作2:“2 t g c”(不含双引号)。表示把所有满足t≤i≤g的ai改为ai+c (1≤t≤g≤N,0≤c≤1000000000)。

操作3:“3 t g”(不含双引号)。询问所有满足t≤i≤g的ai的和模P的值 (1≤t≤g≤N)。 同一行相邻两数之间用一个空格隔开,每行开头和末尾没有多余空格。

样例数据

input

7 43
1 2 3 4 5 6 7
5
1 2 5 5
3 2 4
2 3 7 9
3 1 3
3 4 7


output

2
35
8

注释

【样例说明】

初始时数列为(1,2,3,4,5,6,7)。

经过第1次操作后,数列为(1,10,15,20,25,6,7)。

对第2次操作,和为10+15+20=45,模43的结果是2。

经过第3次操作后,数列为(1,10,24,29,34,15,16}

对第4次操作,和为1+10+24=35,模43的结果是35。

对第5次操作,和为29+34+15+16=94,模43的结果是8。

数据规模与约定

数据编号 1 2 3 4 5 6 7 8 9 10

N= 10 1000 1000 10000 60000 70000 80000 90000 100000 100000

M= 10 1000 1000 10000 60000 70000 80000 90000 100000 100000

时间限制: 1 s 1 \text {s} 1s

空间限制: 256 MB 256 \text {MB} 256MB

#include
using namespace std;
#define LL long long
#define N 100010
int n,m,p,a[N],g,t,MOD,c,z;
struct xds
{
	LL sum[4*N],dat[4*N],mu[4*N];
	void build(int p,int l,int r)
	{
		mu[p]=1;
		if(l==r){sum[p]=a[l];return ;}
		int mid=(l+r)>>1;
		build(p<<1,l,mid);build(p<<1|1,mid+1,r); 
		sum[p]=(sum[p<<1]+sum[p<<1|1]%MOD);
	}
	void spread(int p,int k)
	{
		sum[p<<1]=(mu[p]*sum[p<<1]+dat[p]*(k+1>>1))%MOD;
		sum[p<<1|1]=(mu[p]*sum[p<<1|1]+dat[p]*(k>>1))%MOD;
		mu[p<<1]=mu[p<<1]*mu[p]%MOD;
		mu[p<<1|1]=mu[p<<1|1]*mu[p]%MOD;
		dat[p<<1]=(dat[p<<1]*mu[p]+dat[p])%MOD;
		dat[p<<1|1]=(dat[p<<1|1]*mu[p]+dat[p])%MOD;
		mu[p]=1;
		dat[p]=0;
	}
	void cheng(int p,int l,int r,LL v)
	{
		if(g<=l&&r<=c)
		{
			mu[p]=(mu[p]*v)%MOD;dat[p]=(dat[p]*v)%MOD;sum[p]=(sum[p]*v)%MOD;
			return ;
		}
		spread(p,r-l+1);
		int mid=(l+r)>>1;
		if(g<=mid) cheng(p<<1,l,mid,v);
		if(c>mid) cheng(p<<1|1,mid+1,r,v);
		sum[p]=(sum[p<<1]+sum[p<<1|1])%MOD;
	}
	void jia(int p,int l,int r,LL v)
	{
		if (g<=l&&r<=c){dat[p]=(dat[p]+v)%MOD;sum[p]=(sum[p]+(r-l+1)*v)%MOD;return;}
		spread(p,r-l+1);
		int mid=(l+r)>>1;
		if (g<=mid) jia(p<<1,l,mid,v);
		if (mid<c) jia(p<<1|1,mid+1,r,v);
		sum[p]=(sum[p<<1]+sum[p<<1|1])%MOD;
	}
	LL ask(int p,int l,int r)
	{
		LL ans=0;
		if(g<=l&&r<=c) return sum[p];
		spread(p,r-l+1);
		int mid=(l+r)>>1;
		if (g<=mid) ans+=ask(p<<1,l,mid),ans%=MOD;
		if (mid<c) ans+=ask(p<<1|1,mid+1,r),ans%=MOD;
		sum[p]=(sum[p<<1]+sum[p<<1|1])%MOD;
		return ans;
	}
}tr;
int main()
{
	scanf("%d%d",&n,&MOD);
	for(int i=1;i<=n;i++){scanf("%d",&a[i]);}
	tr.build(1,1,n);
	scanf("%d",&m);
	while(m--)
	{
		scanf("%d%d%d",&t,&g,&c);
		if(t==1){scanf("%d",&z);tr.cheng(1,1,n,z);}
		else if(t==2) {scanf("%d",&z);tr.jia(1,1,n,z);}
		else printf("%lld\n",tr.ask(1,1,n));
	}
	return 0;
}

花神游历各国

题目描述

花神喜欢步行游历各国,顺便虐爆各地竞赛。花神有一条游览路线,它是线型的,也就是说,所有游历国家呈一条线的形状排列,花神对每个国家都有一个喜欢程度(当然花神并不一定喜欢所有国家)。

每一次旅行中,花神会选择一条旅游路线,它在那一串国家中是连续的一段,这次旅行带来的开心值是这些国家的喜欢度的总和,当然花神对这些国家的喜欢程序并不是恒定的,有时会突然对某些国家产生反感,使他对这些国家的喜欢度 δ \delta δ 变为 δ \sqrt \delta δ (可能是花神虐爆了那些国家的 OI,从而感到乏味)。

现在给出花神每次的旅行路线,以及开心度的变化,请求出花神每次旅行的开心值。

输入格式

第一行是一个整数 N N N,表示有 N N N 个国家;

第二行有 N N N 个空格隔开的整数,表示每个国家的初始喜欢度 δ i \delta_i δi

第三行是一个整数 M M M,表示有 M M M 条信息要处理;

第四行到最后,每行三个整数 x , l , r x,l,r x,l,r,当 x = 1 x=1 x=1 时询问游历国家 l l l r r r 的开心值总和,也就是 ∑ i = l r δ i \sum\limits_{i=l}^r \delta_i i=lrδi ,当 x = 2 x=2 x=2 时国家 l l l r r r 中每个国家的喜欢度 δ i \delta_i δi 变为 δ i \sqrt {\delta_i} δi

输出格式

每次 x = 1 x=1 x=1 时,每行一个整数。表示这次旅行的开心度。

####样例

样例输入
4
1 100 5 5
5
1 1 2
2 1 2
1 1 2
2 2 3
1 1 4
样例输出
101
11
11

数据范围与提示

对于全部数据, 1 ≤ n ≤ 1 0 5 , 1 ≤ m ≤ 2 × 1 0 5 , 1 ≤ l ≤ r ≤ n , 0 ≤ δ i ≤ 1 0 9 1\le n\le 10^5,1\le m\le 2\times 10^5,1\le l\le r\le n,0\le \delta_i \le 10^9 1n105,1m2×105,1lrn,0δi109

注:建议使用 sqrt 函数,且向下取整。

#include
using namespace std;
#define N 100010
#define int long long
int a[N],m,n,k,l,flag;
struct huashen
{
	int z[N<<2],y[N<<2],sum[N<<2],maxx[N<<2];
	void build(int p,int l,int r)
	{
		z[p]=l;y[p]=r;
		if(l==r){sum[p]=maxx[p]=a[l]; return;}
		int mid=(r+l)>>1;
		build(p<<1,l,mid);build(p<<1|1,mid+1,r);
		sum[p]=sum[p<<1]+sum[p<<1|1];
		maxx[p]=max(maxx[p<<1],maxx[p<<1|1]);
	}
	int ask(int p,int l,int r,int ans)
	{
		if(z[p]>=l&&y[p]<=r) return sum[p];
		int mid=(z[p]+y[p])>>1;
		if(l<=mid)ans+=ask(p<<1,l,r,0);
		if(r>mid)ans+=ask(p<<1|1,l,r,0);
		return ans;
	}
	void change(int p,int l,int r)
	{
		if(maxx[p]==1||maxx[p]==0) return;
		if(z[p]==y[p])
		{
			sum[p]=sqrt(maxx[p]),maxx[p]=sqrt(maxx[p]);
			return ;
		}
		int mid=(z[p]+y[p])>>1;
		if(l<=mid) change(p<<1,l,r);
		if(r>mid) change(p<<1|1,l,r);
		sum[p]=sum[p<<1]+sum[p<<1|1];
		maxx[p]=max(maxx[p<<1],maxx[p<<1|1]);
	}
}tr;
signed main()
{freopen("travel.in","r",stdin);freopen("travel.out","w",stdout);
	scanf("%lld",&n);
	for(int i=1;i<=n;i++){scanf("%lld",&a[i]);}
	tr.build(1,1,n);scanf("%lld",&m);
	for(int i=1;i<=m;i++)
	{
		scanf("%lld%lld%lld",&flag,&k,&l);
		if(flag==1) printf("%lld\n",tr.ask(1,k,l,0));
		else tr.change(1,k,l);
	}
	return 0;
}

【树链剖分】【zjoi2008】树的统计

题目描述

一树上有 n n n 个节点,编号分别为 1 1 1 n n n,每个节点都有一个权值 w w w。我们将以下面的形式来要求你对这棵树完成一些操作:

  1. CHANGE u t :把节点 u u u 权值改为 t t t
  2. QMAX u v :询问点 u u u 到点 v v v 路径上的节点的最大权值;
  3. QSUM u v :询问点 u u u 到点 v v v 路径上的节点的权值和。

注意:从点 u u u 到点 v v v 路径上的节点包括 u u u v v v 本身。

输入格式

第一行为一个数 n n n,表示节点个数;

接下来 n − 1 n-1 n1 行,每行两个整数 a , b a,b a,b,表示节点 a a a 与节点 b b b 之间有一条边相连;

接下来 n n n 行,每行一个整数,第 i i i 行的整数 w i w_i wi 表示节点 i i i 的权值;

接下来一行,为一个整数 q q q ,表示操作总数;

接下来 q q q 行,每行一个操作,以 CHANGE u tQMAX u vQSUM u v的形式给出。

输出格式

对于每个 QMAXQSUM 的操作,每行输出一个整数表示要求的结果。

样例数据

input

4
1 2
2 3
4 1
4 2 1 3
12
QMAX 3 4
QMAX 3 3
QMAX 3 2
QMAX 2 3
QSUM 3 4
QSUM 2 1
CHANGE 1 5
QMAX 3 4
CHANGE 3 6
QMAX 3 4
QMAX 2 4
QSUM 3 4

output

4
1
2
2
10
6
5
6
5
16

数据规模与约定

对于 100 % 100\% 100% 的数据,有 1 ≤ n ≤ 3 × 1 0 4 , 0 ≤ q ≤ 2 × 1 0 5 1\le n \le 3\times 10^4, 0 \le q \le 2\times 10^5 1n3×104,0q2×105。中途操作中保证每个节点的权值 w w w − 30000 -30000 30000 30000 30000 30000 之间。

时间限制: 1 s 1 \text {s} 1s

空间限制: 256 MB 256 \text {MB} 256MB

#include
using namespace std;
#define N 100010
#define int long long
int v[N],shen[N],size[N],die[N],pos[N],bl[N],a,b,n,q,sz,lin[N],tot;
char ch[10];
struct node{int y,next;}e[N<<1];
struct node2{int l,r,mx,sum;}t[N];
void lian(int x,int y) {e[++tot].next=lin[x];lin[x]=tot;e[tot].y=y;}
void dfs1(int x) 
{
	size[x]=1;
	for(int i=lin[x];i;i=e[i].next)
	{
		if(e[i].y==die[x]) continue;
		shen[e[i].y]=shen[x]+1;die[e[i].y]=x;
		dfs1(e[i].y);
		size[x]+=size[e[i].y];
	}
} 
void dfs2(int x,int chain,int k)
{
	sz++;pos[x]=sz;bl[x]=chain;
	for(int i=lin[x];i;i=e[i].next) if(shen[e[i].y]>shen[x]&&size[e[i].y]>size[k]) k=e[i].y;
	if(k==0) return ;
	dfs2(k,chain,0);
	for(int i=lin[x];i;i=e[i].next) if(shen[e[i].y]>shen[x]&&k!=e[i].y){dfs2(e[i].y,e[i].y,0);}
}
void build(int k,int l,int r)
{
	t[k].l=l;t[k].r=r;
	if(l==r) return ;
	int mid=(l+r)>>1;
	build(k*2,l,mid);build(k*2+1,mid+1,r); 
}
void cr(int k,int x,int y)
{
	int l=t[k].l,r=t[k].r,mid=(l+r)>>1;
	if(l==r) {t[k].sum=t[k].mx=y;return ;}
	if(x<=mid) cr(k*2,x,y);
	else cr(k*2+1,x,y);
	t[k].sum=t[k*2].sum+t[k*2+1].sum;t[k].mx=max(t[k*2].mx,t[k*2+1].mx);
}
int he(int k,int x,int y)
{
	int l=t[k].l,r=t[k].r,mid=(l+r)>>1;
	if(l==x&&y==r) return t[k].sum;
	if(y<=mid) return he(k*2,x,y);
	else if(x>mid) return he(k*2+1,x,y);
	else return he(k*2,x,mid)+he(k*2+1,mid+1,y);
}
int zuida(int k,int x,int y)
{
	int l=t[k].l,r=t[k].r,mid=(l+r)>>1;
	if(l==x&&y==r) return t[k].mx;
	if(y<=mid) return zuida(k*2,x,y);
	else if(x>mid) return zuida(k*2+1,x,y);
	else return max(zuida(k*2,x,mid),zuida(k*2+1,mid+1,y));
}
int qsum(int x,int y,int sum)
{
    while(bl[x]!=bl[y])
    {
		if(shen[bl[x]]<shen[bl[y]]) swap(x,y);
		sum+=he(1,pos[bl[x]],pos[x]);x=die[bl[x]];
	}
	if(pos[x]>pos[y]) swap(x,y);
	sum+=he(1,pos[x],pos[y]);
    return sum;
}
int qmax(int x,int y,int mx)
{
    while(bl[x]!=bl[y])
    {
		if(shen[bl[x]]<shen[bl[y]]) swap(x,y);
		mx=max(mx,zuida(1,pos[bl[x]],pos[x]));
		x=die[bl[x]];
	}
	if(pos[x]>pos[y]) swap(x,y);
    return max(mx,zuida(1,pos[x],pos[y]));
}
signed main()
{
	scanf("%lld",&n);
	for(int i=1;i<n;i++){scanf("%lld%lld",&a,&b);lian(a,b);lian(b,a);}
	for(int i=1;i<=n;i++){scanf("%lld",&v[i]);}
	dfs1(1);dfs2(1,1,0);build(1,1,n);
    for(int i=1;i<=n;i++) cr(1,pos[i],v[i]);
	scanf("%lld",&q);
    for(int i=1;i<=q;i++) 
    {
		scanf("%s%lld%lld",ch,&a,&b);
        if(ch[1]=='H') {v[a]=b;cr(1,pos[a],b);}
        if(ch[1]=='M') printf("%lld\n",qmax(a,b,-1178669866));
        if(ch[1]=='S') printf("%lld\n",qsum(a,b,0));
    }
	return 0;
}

JSOI2008最大数

题目描述

给定一个正整数数列 a 1 , a 2 , a 3 , ⋯   , a n a_1, a_2, a_3, \cdots , a_n a1,a2,a3,,an,每一个数都在 0 ∼ p – 1 0 \sim p–1 0p1 之间。可以对这列数进行两种操作:

  • 添加操作:向序列后添加一个数,序列长度变成 $ n + 1 $;

  • 询问操作:询问这个序列中最后 L L L 个数中最大的数是多少。

程序运行的最开始,整数序列为空。写一个程序,读入操作的序列,并输出询问操作的答案。

输入格式

第一行有两个正整数 m , p m,p m,p,意义如题目描述;

接下来 $ m $ 行,每一行表示一个操作。如果该行的内容是 Q L,则表示这个操作是询问序列中最后 L L L 个数的最大数是多少;如果是 A t,则表示向序列后面加一个数,加入的数是 ( t + a )   m o d   p (t+a)\bmod p (t+a)modp。其中, t t t 是输入的参数, a a a 是在这个添加操作之前最后一个询问操作的答案(如果之前没有询问操作,则 $ a = 0 $)。

第一个操作一定是添加操作。对于询问操作, L > 0 L\gt 0 L>0 且不超过当前序列的长度。

输出格式

对于每一个询问操作,输出一行。该行只有一个数,即序列中最后 $ L $ 个数的最大数。

样例

样例输入
10 100
A 97
Q 1
Q 1
A 17
Q 2
A 63
Q 1
Q 1
Q 3
A 99
样例输出
97
97
97
60
60
97
样例说明

最后的序列是 97 , 14 , 60 , 96 97,14,60,96 97,14,60,96

数据范围与提示

对于全部数据, 1 ≤ m ≤ 2 × 1 0 5 , 1 ≤ p ≤ 2 × 1 0 9 , 0 ≤ t < p 1\le m\le 2\times 10^5,1\le p\le 2\times 10^9,0\le t\lt p 1m2×105,1p2×109,0t<p

#include
using namespace std;
#define N 200010
char flag;
int biaoji,root,cnt,num,m,n,p,c,a;
struct Tree
{
	int rs[N<<1],ls[N<<1],v[N<<1];
	int ask(int p,int kaishi,int jieshu,int l,int r)
	{
		if(l>=kaishi&&r<=jieshu) return v[p];
		int mid=(l+r)>>1,maxn=0;
		if(kaishi<=mid) maxn=ask(ls[p],kaishi,jieshu,l,mid);
		if(jieshu>mid) maxn=max(maxn,ask(rs[p],kaishi,jieshu,mid+1,r));
		return maxn;
	}
	void charu(int &p,int dian,int l,int r,int val)
	{
		if(!p) p=++cnt;
		if(l==r) {v[p]=val;return;}
		int mid=(l+r)>>1;
		if(dian<=mid) charu(ls[p],dian,l,mid,val);
		else charu(rs[p],dian,mid+1,r,val);
		v[p]=max(v[ls[p]],v[rs[p]]);	
	}

}t;
int main()
{
	scanf("%d%d",&m,&p);
	for(int i=1;i<=m;i++)
	{
		scanf("%s%d",&flag,&a);
		if(flag=='Q') {biaoji=t.ask(root,num-a+1,num,1,m);printf("%d\n",biaoji);}
		else {t.charu(root,num+1,1,m,(biaoji+a)%p);num++;}
	}
	return 0;
}

你可能感兴趣的:(水题,学习,c++)