这题不是线段树的模板题吗,其实可以这样理解QAQ:
A+B=(A/gcd(A,B)+B/gcd(A,b))*gcd(A,B) (gcd指最大公约数)
所以100+的线段树就出现了
#include<iostream>
#include<cstdio>
#include<cmath>
#include<string.h>
using namespace std;
//为了防止超时,先写一个读入输出优化
int read(void)//读入优化
{
int x=0;int w=1; char ch=0;
while(!isdigit(ch)){if(ch=='-')w=-1;if(ch=='+')w=1;ch=getchar();}
while(isdigit(ch))x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
return w*x;
}
void write(int x)//输出优化
{
if(x<0){putchar('-');x=-x;}
if(x>9)write(x/10);
putchar(x%10+'0');
}
int Left(int a){return a*2;}
int Right(int a){return a*2+1;}
int i,j,k,m,n,a[1000001],ans[1000001],Lazy[1000001],lazy[1000001];
void PushUp(int now)//记录答案
{
ans[now]=(ans[Left(now)]+ans[Right(now)]);
}
void Build(int now,int l,int r)//建树
{
if(l==r){ans[now]=a[l];return;}
int mid=(l+r)/2;
Build(Left(now),l,mid);
Build(Right(now),mid+1,r);
PushUp(now);
}
void Function(int now,int l,int r,int Add)//加法的运算
{
Lazy[now]+=Add;
ans[now]+=Add*(r-l+1);
}
void PushDown(int now,int l,int r)//向下推懒标记-加法
{
int mid=(l+r)/2;
Function(Left(now),l,mid,Lazy[now]);
Function(Right(now),mid+1,r,Lazy[now]);
Lazy[now]=0;
}
void function(int now,int l,int r,int Add)//惩罚的运算
{
Lazy[now]*=Add;
lazy[now]*=Add;
ans[now]*=Add;
}
void Push_Down(int now,int l,int r)//向下推懒标记-乘法
{
int mid=(l+r)/2;
function(Left(now),l,mid,lazy[now]);
function(Right(now),mid+1,r,lazy[now]);
lazy[now]=1;
}
void Data(int NowL,int NowR,int l,int r,int now,int Add)//对于某个区间的数都加上一个数
{
if(NowL<=l&&NowR>=r)
{
ans[now]+=Add*(r-l+1);
Lazy[now]+=Add;
return;
}
Push_Down(now,l,r);
PushDown(now,l,r);
int mid=(l+r)/2;
if(NowL<=mid)Data(NowL,NowR,l,mid,Left(now),Add);
if(NowR>mid)Data(NowL,NowR,mid+1,r,Right(now),Add);
PushUp(now);
}
void data(int NowL,int NowR,int l,int r,int now,int Add)//对于某个区间的数都乘上一个数
{
if(NowL<=l&&NowR>=r)
{
ans[now]*=Add;
Lazy[now]*=Add;
lazy[now]*=Add;
return;
}
Push_Down(now,l,r);
PushDown(now,l,r);
int mid=(l+r)/2;
if(NowL<=mid)data(NowL,NowR,l,mid,Left(now),Add);
if(NowR>mid)data(NowL,NowR,mid+1,r,Right(now),Add);
PushUp(now);
}
int Query(int NowL,int NowR,int l,int r,int now)//区间查询总和
{
int Return=0;
if(NowL<=l&&NowR>=r)return ans[now];
int mid=(l+r)/2;
Push_Down(now,l,r);
PushDown(now,l,r);
if(NowL<=mid)Return+=Query(NowL,NowR,l,mid,Left(now));
if(NowR>mid)Return+=Query(NowL,NowR,mid+1,r,Right(now));
return Return;
}
int gcd(int a,int b)//最大公约数
{
if(b==0)return a;
return gcd(b,a%b);
}
int main()
{
n=2;//一共两个数
int A=read(),B=read();
Build(1,1,n);//建树
for(i=1;i<=n*4;i++)lazy[i]=1;//乘法懒标记的初值是1
int Gcd=gcd(A,B);//计算出gcd(A,B)的值
Data(1,1,1,n,1,A/Gcd);//对于1-1的区间都加上A/gcd(A,B)
Data(2,2,1,n,1,B/Gcd);//对于2-2的区间都加上B/gcd(A,B)
data(1,2,1,n,1,Gcd);//对于1-2的区间都乘上gcd(A,B)
write(Query(1,2,1,n,1));//输出1-2这个区间的总和
}
这题其实就是P3373的升级版QAQ
再介绍一种比较cai的解决A+B问题的方法
旋转卡壳
这样一个优四个点组成的三角形中最长边为sqrt((a+b)2+0.32)可以发现就算a+b=0时0.32也可以忽略不计,自然只要用取整的方法计算就行了
但是,这样计算出来的值其实为abs(a+b)然而这与实际要计算的值不同
可以发现,当结果为负数时,b点一定在a点的右边,所以自然只要用叉积判断一下,十分的简单QAQ
#include<iostream>
#include<iomanip>
#include<cstdio>
#include<cmath>
#include<string.h>
#include<algorithm>
using namespace std;
struct point//定义一个点的类
{double x,y;};
double sqr(double a)
{return a*a;}
int n,h,t,a,b,spj;
point Point[100001],check[100001];
bool cmp(point a,point b)
{
if(a.y!=b.y)return a.y>b.y;
return a.x>b.x;
}
double far(point a,point b)
{return sqrt(sqr(a.x-b.x)+sqr(a.y-b.y));}
double dis(point a,point b)
{return sqrt(sqr(a.x-b.x)+sqr(a.y-b.y));}
double side(point a,point b,point c)
{return a.x*b.y+a.y*c.x+b.x*c.y-a.x*c.y-a.y*b.x-b.y*c.x;}
double cha(point a,point b,point c)
{return(b.x-a.x)*(c.y-a.y)-(c.x-a.x)*(b.y-a.y);}
void Around(int n)//一个凸包模板
{
h=n+1;
t=n+1;
int i;
check[t++]=Point[1];
for(i=1;i<=n;i++)
{
check[t]=Point[i];
if(side(check[n],Point[i],Point[i+1]))break;
}
if(n<2)return;
check[++t]=check[--h]=Point[++i];
if(side(check[n+1],check[n+2],check[n+3])<0)swap(check[n+1],check[n+2]);
for(++i;i<=n;i++)
{
if(side(check[h+1],check[h],Point[i])<0&&side(check[t-1],check[t],Point[i])>0)continue;
while(t-h>1&&side(check[h+1],check[h],Point[i])>=0)++h;
check[--h]=Point[i];
while(t-h>1&&side(check[t-1],check[t],Point[i])<=0)--t;
check[++t]=Point[i];
}
}
void solve(int num)//一个旋转卡壳模板
{
int max_y=-1e6,min_y=1e6,i,j;
int max_y_id,min_y_id;
for(i=1;i<=num;i++)
{
if(check[i].y>max_y)max_y=check[i].y,max_y_id=i;
if(check[i].y<min_y)min_y=check[i].y,min_y_id=i;
}
double ans=dis(check[max_y_id],check[max_y_id]);
check[num+1]=check[1];
for(j=1;j<=num;j++,min_y_id=min_y_id%num+1)
{
while(cha(check[min_y_id+1],check[max_y_id+1],check[min_y_id])>cha(check[min_y_id+1],check[max_y_id],check[min_y_id]))max_y_id=max_y_id%num+1;
ans=max(ans,dis(check[max_y_id],check[min_y_id]));
ans=max(ans,dis(check[max_y_id],check[min_y_id+1]));
}
cout<<(long long)(ans)*spj;//输出
}
int main()
{
scanf("%d%d",&a,&b);
if(a==-b)//其实没有用的特判
{
printf("0");
return 0;
}
n=4;
Point[1].x=a;//放入四个点的坐标
Point[1].y=0;
Point[2].x=-b;
Point[2].y=0;
Point[3].x=a;
Point[3].y=0.3;
Point[4].x=a;
Point[4].y=-0.3;
spj=1;
if(side(Point[3],Point[4],Point[2])>0)spj=-1;//叉积判断正负
sort(Point+1,Point+n+1,cmp);//求出凸包要先排序
Around(n);//再处理
int i;
for(i=h;i<t;i++)
check[i-h+1]=check[i];//为了方便用旋转卡壳,先将数组放到开头
solve(t-h);//计算直径
return 0;
}
--------------------------华丽的分割线--------------------------------------
提供几种做A+B的方法
- 1:做一个只有两个点的图(有点权),和一条把它们连在一起的边,根据点权跑一个最短路
- 2:当做字符串读入,这样就不会炸了
- 3:写个高精吧,两倍经验
- 4:因为加法符合交换律,交换==翻转,自然就会先到平衡树了QAQ
- 5:二分答案QAQ,这个没什么好讲的
- 6:线段树都可以树状数组自然也不服QAQ
- n:
自动AC机
--------------------------华丽的分割线*2-----------------------------------
最后,祝大家A了这道题