题目:https://www.luogu.org/problemnew/show/P2221

似乎按点来算贡献很方便,但我抱住一篇没有这样的题解磕了两天...

以下转载:


题意:维护一段数列 支持区间加和求区间所有子区间的和的和

一看就知道要用线段树 于是用sum表示区间所有子区间的和的和 但是知道两个区间的sum并不能求出这两个区间并起来之后的sum 这时我们可以手玩一下

sum(1 2 3 4)=(1)+(2)+(1 2)+(3)+(4)+(3 4)+(2 3)+(1 2 3)+(2 3 4)+(1 2 3 4)竖着写在纸上 就会发现除了sum(1 2)和sum(3 4)外 所有包含左区间右端点的区间和(rsum)恰被算了len(右区间)次 同理包含右区间左端点的区间和(lsum)被算了len(左区间)次

于是 我们得到了sum(a b)=sum(a)+sum(b)+rsum(a)·len(b)+lsum(b)·len(a)

那么如何维护lsum和rsum呢 显然lsum(a b)=lsum(a)+lsum(b)+区间和(a)*len(b) rsum同理

至此维护操作已经没问题了 pushdown操作就简单很多了 当一个区间整体加a时 它的sum增加了a(n+2(n-1)+3(n-2)+...+n)也就是a((1+...+n)n-(1^2+...+(n-1)^2)-(1+...(n-1))) lsum rsum 同理 最后注意取GCD


看样子很精妙,似乎也挺好写的;

然而因为还是理解不到位,犯了众多细节的错误,调了两天,令人疯狂:

1.线段树上的每个点是车站之间的一段,所以读入的n和r都要-1;

2.需要另外记录纯粹的区间和(s),用于计算sum,lsum,rsum等等,而不是直接用sum一概计算;

3.采用那样的build写法,竟然一度忘记在pushup里更新len,调了半天;

*4.注意这个背景下的线段树与平常的线段树写法有所不同:

  平常的线段树在修改、查询时都会传下去一个目标区间,一般来说这个目标区间不用更改,判断只需看是否在其内部就可以;

  但这道题的计算是需要用到目标区间的,所以在把操作下放到子区间时,其目标区间也必须相应有所修改,否则在计算时就会大错特错;

  也正因此,必须进行判断:目标区间是完全在左儿子,还是完全在右儿子,还是在中间;不同的情况中目标区间的修改也有所不同。

虽然这种写法似乎不是很优秀,但在线段树中痛苦挣扎的过程中,我认识到就算是线段树也不能不求甚解,靠习惯打板子;必须根据题目灵活调整,否则就会出现错误。

错误版本1:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int const maxn=4e5+;
int n,m;
struct N{
int sum,lsum,rsum,len,lzy;
int s;//!
}t[maxn];
int gcd(int x,int y){return y?gcd(y,x%y):x;}
void yf(int x,int nn)//nn
{
int y=(+nn)*nn/;
int a=x,b=y;
if(a<b)swap(a,b);
int c=gcd(a,b);
printf("%d/%d\n",x/c,y/c);
}
int cala(int x)//add
{
int nn=t[x].len;
int n=nn-;
return (+nn)*nn/*nn-(+n)*n/-n*(n+)*(n*+)/;//
}
int calb(int x)//lsum,rsum add
{
int nn=t[x].len;
return (+nn)*nn/;
}
void pushup(int x)
{
int ls=(x<<),rs=(x<<|);
t[x].len=t[x<<].len+t[x<<|].len;
t[x].s=t[x<<].s+t[x<<|].s;
t[x].sum=t[ls].sum+t[rs].sum+t[ls].rsum*t[rs].len+t[rs].sum*t[ls].len;
t[x].lsum=t[ls].lsum+t[rs].lsum+t[ls].s*t[rs].len;//s
t[x].rsum=t[ls].rsum+t[rs].rsum+t[rs].s*t[ls].len;
}
void build(int l,int r,int x)
{
if(l==r)
{
t[x].len=;
return;
}
int mid=((l+r)>>);
build(l,mid,x<<);
build(mid+,r,x<<|);
pushup(x);
}
void pushdown(int x)
{
if(t[x].lzy)
{
int v=t[x].lzy;t[x].lzy=;
int ls=(x<<),rs=(x<<|);
t[ls].s+=t[ls].len*v;
t[ls].sum+=cala(ls)*v;
t[ls].lsum+=calb(ls)*v;
t[ls].rsum+=calb(ls)*v;
t[ls].lzy+=v;
t[rs].s+=t[rs].len*v;
t[rs].sum+=cala(rs)*v;
t[rs].lsum+=calb(rs)*v;
t[rs].rsum+=calb(rs)*v;
t[rs].lzy+=v;
}
}
void add(int l,int r,int L,int R,int v,int x)
{
if(l>=L&&r<=R)
{
t[x].s+=t[x].len*v;
t[x].sum+=cala(x)*v;
t[x].lsum+=calb(x)*v;
t[x].rsum+=calb(x)*v;
t[x].lzy+=v;
return;
}
pushdown(x);
int mid=((l+r)>>);
if(L<=mid)add(l,mid,L,R,v,x<<);
if(R>mid)add(mid+,r,L,R,v,x<<|);
pushup(x);
}
int qs(int l,int r,int L,int R,int x)
{
if(l>=L&&r<=R)return t[x].s;
pushdown(x);
int ret=;
int mid=((l+r)>>);
if(L<=mid)ret+=qs(l,mid,L,R,x<<);
if(R>mid)ret+=qs(mid+,r,L,R,x<<|);
return ret;
}
int qls(int l,int r,int L,int R,int x)
{
if(l>=L&&r<=R)return t[x].lsum;
pushdown(x);
int mid=((l+r)>>);
if(L>mid)return qls(mid+,r,L,R,x<<|);
if(R<=mid)return qls(l,mid,L,R,x<<);
return qls(mid+,r,L,R,x<<|)+qls(l,mid,L,R,x<<)+qs(l,mid,L,R,x<<)*t[x<<|].len;
}
int qrs(int l,int r,int L,int R,int x)
{
if(l>=L&&r<=R)return t[x].rsum;
pushdown(x);
int mid=((l+r)>>);
if(L>mid)return qrs(mid+,r,L,R,x<<|);
if(R<=mid)return qrs(l,mid,L,R,x<<);
return qrs(mid+,r,L,R,x<<|)+qrs(l,mid,L,R,x<<)+qs(mid+,r,L,R,x<<|)*t[x<<].len;
}
int query(int l,int r,int L,int R,int x)
{
if(l>=L&&r<=R)return t[x].sum;
pushdown(x);
int mid=((l+r)>>);
if(L>mid)return query(mid+,r,L,R,x<<|);
if(R<=mid)return query(l,mid,L,R,x<<);
return query(mid+,r,L,R,x<<|)+query(l,mid,L,R,x<<)
// +qrs(l,mid,L,R,x<<1)*t[x<<1|1].len+qls(mid+1,r,L,R,x<<1|1)*t[x<<1].len;
+qrs(l,mid,L,R,x<<)*(R-mid)+qls(mid+,r,L,R,x<<|)*(mid-L+);
}
int main()
{
scanf("%d%d",&n,&m);
n--;//
build(,n,);
for(int i=,l,r,v;i<=m;i++)
{
char dc;
cin>>dc;
scanf("%d%d",&l,&r);//车站而非间距
r--;
if(dc=='C')
{
scanf("%d",&v);
add(,n,l,r,v,);
}
if(dc=='Q')
{
int k=query(,n,l,r,);
yf(k,r-l+);
}
}
return ;
}

错误版本2:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long ll;
ll const maxn=5e5+;
ll n,m;
struct N{
ll sum,lsum,rsum,len,lzy;
ll s;//!
}t[maxn];
ll gcd(ll x,ll y){return y?gcd(y,x%y):x;}
void yf(ll x,ll nn)//nn
{
ll y=(+nn)*nn/;
ll a=x,b=y;
if(a<b)swap(a,b);
ll c=gcd(a,b);
printf("%lld/%lld\n",x/c,y/c);
}
ll cala(ll x)//add
{
ll nn=t[x].len;
ll n=nn-;
return (+nn)*nn/*nn-(+n)*n/-n*(n+)*(n*+)/;//
}
ll calb(ll x)//lsum,rsum add
{
ll nn=t[x].len;
return (+nn)*nn/;
}
void pushup(ll x)
{
ll ls=(x<<),rs=(x<<|);
// t[x].len=t[x<<1].len+t[x<<1|1].len;
t[x].s=t[x<<].s+t[x<<|].s;
t[x].sum=t[ls].sum+t[rs].sum+t[ls].rsum*t[rs].len+t[rs].lsum*t[ls].len;
t[x].lsum=t[ls].lsum+t[rs].lsum+t[ls].s*t[rs].len;//s
t[x].rsum=t[ls].rsum+t[rs].rsum+t[rs].s*t[ls].len;
}
void build(ll l,ll r,ll x)
{
t[x].len=r-l+;
if(l==r)
{
// t[x].len=1;
return;
}
ll mid=((l+r)>>);
build(l,mid,x<<);
build(mid+,r,x<<|);
pushup(x);
}
void pushdown(ll x)
{
if(t[x].lzy)
{
ll v=t[x].lzy;t[x].lzy=;
ll ls=(x<<),rs=(x<<|);
t[ls].s+=t[ls].len*v;
t[ls].sum+=cala(ls)*v;
t[ls].lsum+=calb(ls)*v;
t[ls].rsum+=calb(ls)*v;
t[ls].lzy+=v;
t[rs].s+=t[rs].len*v;
t[rs].sum+=cala(rs)*v;
t[rs].lsum+=calb(rs)*v;
t[rs].rsum+=calb(rs)*v;
t[rs].lzy+=v;
}
}
void add(ll l,ll r,ll L,ll R,ll v,ll x)
{
if(l>=L&&r<=R)//
{
t[x].s+=t[x].len*v;
t[x].sum+=cala(x)*v;
t[x].lsum+=calb(x)*v;
t[x].rsum+=calb(x)*v;
t[x].lzy+=v;
return;
}
pushdown(x);
ll mid=((l+r)>>);
if(R<=mid)add(l,mid,L,R,v,x<<);
else if(L>mid)add(mid+,r,L,R,v,x<<|);
else
{
add(l,mid,L,R,v,x<<);
add(mid+,r,L,R,v,x<<|);
}
pushup(x);
}
ll qs(ll l,ll r,ll L,ll R,ll x)
{
if(l>=L&&r<=R)return t[x].s;//
pushdown(x);
ll mid=((l+r)>>);
if(R<=mid)return qs(l,mid,L,R,x<<);
if(L>mid)return qs(mid+,r,L,R,x<<|);
return qs(l,mid,L,R,x<<)+qs(mid+,r,L,R,x<<|);
}
ll qls(ll l,ll r,ll L,ll R,ll x)
{
if(l>=L&&r<=R)return t[x].lsum;//
pushdown(x);
ll mid=((l+r)>>);
if(L>mid)return qls(mid+,r,L,R,x<<|);
if(R<=mid)return qls(l,mid,L,R,x<<);
return qls(mid+,r,L,R,x<<|)+qls(l,mid,L,R,x<<)+qs(l,mid,L,R,x<<)*(R-mid);
}
ll qrs(ll l,ll r,ll L,ll R,ll x)
{
if(l>=L&&r<=R)return t[x].rsum;//
pushdown(x);
ll mid=((l+r)>>);
if(L>mid)return qrs(mid+,r,L,R,x<<|);
if(R<=mid)return qrs(l,mid,L,R,x<<);
return qrs(mid+,r,L,R,x<<|)+qrs(l,mid,L,R,x<<)+qs(mid+,r,L,R,x<<|)*(mid-L+);
}
ll query(ll l,ll r,ll L,ll R,ll x)
{
// cout<<l<<" "<<r<<endl;
// printf("*%lld %lld\n",L,R);
if(l>=L&&r<=R)return t[x].sum;//
pushdown(x);
ll mid=((l+r)>>);
if(L>mid)return query(mid+,r,L,R,x<<|);
if(R<=mid)return query(l,mid,L,R,x<<);
return query(mid+,r,L,R,x<<|)+query(l,mid,L,R,x<<)
+qrs(l,mid,L,R,x<<)*(R-mid)+qls(mid+,r,L,R,x<<|)*(mid-L+);
}
int main()
{
scanf("%lld%lld",&n,&m);
n--;//
build(,n,);
for(ll i=,l,r,v;i<=m;i++)
{
char dc;
cin>>dc;
scanf("%lld%lld",&l,&r);//车站而非间距
r--;
if(dc=='C')
{
scanf("%lld",&v);
add(,n,l,r,v,);
}
if(dc=='Q')
{
ll k=query(,n,l,r,);
yf(k,r-l+);
}
}
return ;
}

代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long ll;
ll const maxn=5e5+;
ll n,m;
struct N{
ll sum,lsum,rsum,len,lzy;
ll s;//!
}t[maxn];
ll gcd(ll x,ll y){return y?gcd(y,x%y):x;}
void yf(ll x,ll nn)//nn
{
ll y=(+nn)*nn/;
ll a=x,b=y;
if(a<b)swap(a,b);
ll c=gcd(a,b);
printf("%lld/%lld\n",x/c,y/c);
}
ll cala(ll x)//add
{
ll nn=t[x].len;
ll n=nn-;
return (+nn)*nn/*nn-(+n)*n/-n*(n+)*(n*+)/;//
}
ll calb(ll x)//lsum,rsum add
{
ll nn=t[x].len;
return (+nn)*nn/;
}
void pushup(ll x)
{
ll ls=(x<<),rs=(x<<|);
t[x].len=t[x<<].len+t[x<<|].len;
t[x].s=t[x<<].s+t[x<<|].s;
t[x].sum=t[ls].sum+t[rs].sum+t[ls].rsum*t[rs].len+t[rs].lsum*t[ls].len;
t[x].lsum=t[ls].lsum+t[rs].lsum+t[ls].s*t[rs].len;//s
t[x].rsum=t[ls].rsum+t[rs].rsum+t[rs].s*t[ls].len;
}
void build(ll l,ll r,ll x)
{
// t[x].len=r-l+1;
if(l==r)
{
t[x].len=;
return;
}
ll mid=((l+r)>>);
build(l,mid,x<<);
build(mid+,r,x<<|);
pushup(x);
}
void pushdown(ll x)
{
if(t[x].lzy)
{
ll v=t[x].lzy;t[x].lzy=;
ll ls=(x<<),rs=(x<<|);
t[ls].s+=t[ls].len*v;
t[ls].sum+=cala(ls)*v;
t[ls].lsum+=calb(ls)*v;
t[ls].rsum+=calb(ls)*v;
t[ls].lzy+=v;
t[rs].s+=t[rs].len*v;
t[rs].sum+=cala(rs)*v;
t[rs].lsum+=calb(rs)*v;
t[rs].rsum+=calb(rs)*v;
t[rs].lzy+=v;
}
}
void add(ll l,ll r,ll L,ll R,ll v,ll x)
{
if(l>=L&&r<=R)//
{
t[x].s+=t[x].len*v;
t[x].sum+=cala(x)*v;
t[x].lsum+=calb(x)*v;
t[x].rsum+=calb(x)*v;
t[x].lzy+=v;
return;
}
pushdown(x);
ll mid=((l+r)>>);
if(R<=mid)add(l,mid,L,R,v,x<<);
else if(L>mid)add(mid+,r,L,R,v,x<<|);
else
{
add(l,mid,L,mid,v,x<<);
add(mid+,r,mid+,R,v,x<<|);
}
pushup(x);
}
ll qs(ll l,ll r,ll L,ll R,ll x)
{
if(l==L&&r==R)return t[x].s;//
pushdown(x);
ll mid=((l+r)>>);
if(R<=mid)return qs(l,mid,L,R,x<<);
if(L>mid)return qs(mid+,r,L,R,x<<|);
return qs(l,mid,L,mid,x<<)+qs(mid+,r,mid+,R,x<<|);
}
ll qls(ll l,ll r,ll L,ll R,ll x)
{
if(l==L&&r==R)return t[x].lsum;//
pushdown(x);
ll mid=((l+r)>>);
if(L>mid)return qls(mid+,r,L,R,x<<|);
if(R<=mid)return qls(l,mid,L,R,x<<);
return qls(mid+,r,mid+,R,x<<|)+qls(l,mid,L,mid,x<<)
+qs(l,mid,L,mid,x<<)*(R-mid);
}
ll qrs(ll l,ll r,ll L,ll R,ll x)
{
if(l==L&&r==R)return t[x].rsum;//
pushdown(x);
ll mid=((l+r)>>);
if(L>mid)return qrs(mid+,r,L,R,x<<|);
if(R<=mid)return qrs(l,mid,L,R,x<<);
return qrs(mid+,r,mid+,R,x<<|)+qrs(l,mid,L,mid,x<<)
+qs(mid+,r,mid+,R,x<<|)*(mid-L+);
}
ll query(ll l,ll r,ll L,ll R,ll x)//注意L,R要纳入计算
{
// cout<<l<<" "<<r<<endl;
// printf("*%lld %lld\n",L,R);
if(l==L&&r==R)return t[x].sum;//
pushdown(x);
ll mid=((l+r)>>);
if(L>mid)return query(mid+,r,L,R,x<<|);
if(R<=mid)return query(l,mid,L,R,x<<);
return query(mid+,r,mid+,R,x<<|)+query(l,mid,L,mid,x<<)
+qrs(l,mid,L,mid,x<<)*(R-mid)+qls(mid+,r,mid+,R,x<<|)*(mid-L+);//!
}
int main()
{
scanf("%lld%lld",&n,&m);
n--;//
build(,n,);
for(ll i=,l,r,v;i<=m;i++)
{
char dc;
cin>>dc;
scanf("%lld%lld",&l,&r);//车站而非间距
r--;
if(dc=='C')
{
scanf("%lld",&v);
add(,n,l,r,v,);
}
if(dc=='Q')
{
ll k=query(,n,l,r,);
yf(k,r-l+);
}
}
return ;
}

HAOI2012高速公路——子区间计算的更多相关文章

  1. BZOJ 2752:[HAOI2012]高速公路(road)(线段树)

    [HAOI2012]高速公路(road) Description Y901高速公路是一条重要的交通纽带,政府部门建设初期的投入以及使用期间的养护费用都不低,因此政府在这条高速公路上设立了许多收费站.Y ...

  2. BZOJ2752: [HAOI2012]高速公路(road)

    2752: [HAOI2012]高速公路(road) Time Limit: 20 Sec  Memory Limit: 128 MBSubmit: 608  Solved: 199[Submit][ ...

  3. BZOJ 2752: [HAOI2012]高速公路(road)( 线段树 )

    对于询问[L, R], 我们直接考虑每个p(L≤p≤R)的贡献,可以得到 然后化简一下得到 这样就可以很方便地用线段树, 维护一个p, p*vp, p*(p+1)*vp就可以了 ----------- ...

  4. 【线段树】BZOJ2752: [HAOI2012]高速公路(road)

    2752: [HAOI2012]高速公路(road) Time Limit: 20 Sec  Memory Limit: 128 MBSubmit: 1621  Solved: 627[Submit] ...

  5. BZOJ 2752: [HAOI2012]高速公路(road) [线段树 期望]

    2752: [HAOI2012]高速公路(road) Time Limit: 20 Sec  Memory Limit: 128 MBSubmit: 1219  Solved: 446[Submit] ...

  6. P2221 [HAOI2012]高速公路(线段树)

    P2221 [HAOI2012]高速公路 显然答案为 $\dfrac{\sum_{i=l}^r\sum_{j=l}^{r}dis[i][j]}{C_{r-l+1}^2}$ 下面倒是挺好算,组合数瞎搞 ...

  7. [Luogu 2221] HAOI2012 高速公路

    [Luogu 2221] HAOI2012 高速公路 比较容易看出的线段树题目. 由于等概率,期望便转化为 子集元素和/子集个数. 每一段l..r中,子集元素和为: \(\sum w_{i}(i-l+ ...

  8. bzoj 2752: [HAOI2012]高速公路(road)

    Description Y901高速公路是一条重要的交通纽带,政府部门建设初期的投入以及使用期间的养护费用都不低,因此政府在这条高速公路上设立了许多收费站.Y901高速公路是一条由N-1段路以及N个收 ...

  9. 【bzoj2752】[HAOI2012]高速公路(road) 线段树

    题目描述 Y901高速公路是一条重要的交通纽带,政府部门建设初期的投入以及使用期间的养护费用都不低,因此政府在这条高速公路上设立了许多收费站.Y901高速公路是一条由N-1段路以及N个收费站组成的东西 ...

随机推荐

  1. T1013 求先序排列 codevs

    http://codevs.cn/problem/1013/  时间限制: 1 s  空间限制: 128000 KB  题目等级 : 黄金 Gold 题解  查看运行结果     题目描述 Descr ...

  2. 51 NOD 1406 and query

    我们知道一个数S会对所有它的子集S'产生1的贡献,但是我们直接枚举子集是 3^(log2 1000000)的,会炸掉:如果直接把每个有1的位变成0往下推也会凉掉,因为这样会有很多重复的. 但是我们发现 ...

  3. Windows下部署多个Tomcat

    编辑bin/startup.bat SET JAVA_HOME=...(JDK所在路径) SET CATALINA_HOME=...(Tomcat解压的路径) 编辑server.xml文件 <! ...

  4. 王立平--Gallery:实现图片的左右滑动

    <span style="font-size:18px;color:#330033;">package com.main; import android.app.Act ...

  5. [WASM Rust] Create and Publish a NPM Package Containing Rust Generated WebAssembly using wasm-pack

    wasm-pack is a tool that seeks to be a one-stop shop for building and working with Rust generated We ...

  6. SolidEdge 如何由装配图快速生成爆炸视图

    建立图纸精灵,组态中选择explode(没有下一步可选了)   点击完成即可绘制爆炸图

  7. POJ 3518 Prime Gap(素数)

    POJ 3518 Prime Gap(素数) id=3518">http://poj.org/problem? id=3518 题意: 给你一个数.假设该数是素数就输出0. 否则输出比 ...

  8. react 创建组件 (一)createClass

    如果你还没有使用ES6语法,那么定义组件,只能使用React.createClass这个helper来创建组件,下面是一段示例: var React = require("react&quo ...

  9. linux系列之-—03 压缩和解压缩命令

    tar命令 解包:tar zxvf FileName.tar 打包:tar czvf FileName.tar DirName gz命令 解压1:gunzip FileName.gz 解压2:gzip ...

  10. HTML初体验

    ios讨论群1群:135718460 HTML是英文Hyper Text Mark-up Language(超文本标记语言)的缩写,它规定了自己的语法规则,用来表示比"文本"更丰富 ...