P2221 [HAOI2012] 高速公路
P2221 [HAOI2012] 高速公路
考场因为读字符滥用 getchar 导致读入混乱爆零,警钟长鸣
思路
区间加区间查考虑线段树。
给出了点和点,我们把每次输入的 \(r\) 减一来转化为边上,接下来考虑概率期望部分。
在 \(l,r\) 之间取,令 \(l\) 和 \(r\) 之间的点的个数为 \(k\),即 \(r-l+1\),取出两个点的概率为 $\frac{1}{k\times (k-1)} $ 即 $\frac{1}{(r-l+1)\times (r-l)} $。
分母就是 \((r-l+1)\times (r-l)\),考虑分子。
那分子就是任意两点之间的权值和,直接计算是 \(n^2\) 的
for(int i{l};i<=r;i++)
for(int j{i};j<=r;j++)
res+=tree.query(i,j,1);
如何优化?每一小段对权值和的贡献和它左端点和右端点所处的位置有关。
每个它左边的点组成线段都能对它计算一次贡献,那么这条线段就会被左边的每个点计算 \(r-i\) 次,从 \(l\) 计算到 \(i\),一共有 \(i-l+1\) 个点,那么统计出来这条线段累计的贡献数就是 \(\displaystyle\sum_{i=l}^{r-1} (r-i)\times (i-l+1)\)。
贡献数再乘上权值 \(\displaystyle\sum_{i=l}^{r-1} (r-i)\times (i-l+1)v_i\)。
把这个式子拆开
\(\displaystyle\sum_{i=l}^{r-1} (r \times i - i^2 -l \times r + l \times i + r-i)v_i\)
\(=\displaystyle\sum_{i=l}^{r-1} ((l+r-1)i-i^2 + r-lr)v_i\)
\(=(l+r-1)\displaystyle\sum_{i=l}^{r-1}iv_i -\displaystyle\sum_{i=l}^{r-1}i^2v_i+(r-lr)\displaystyle\sum_{i=l}^{r-1}v_i\)
发现其实我们需要求出来区间的 \(iv_i\),\(i^2v_i\) 和 \(v_i\) 之和。
用一棵线段树维护这三个值就行。
传递标记时的增量(以 \(iv_i\) 为例):
\(\displaystyle\sum_{i=l}^{r-1}i(v_i+\Delta v)\)
$ =\displaystyle{\sum_{i=l}^{r-1}i v_{i}+\sum_{i=l}^{r-1}i\Delta v }$
那区间增量就是区间 \(i\) 和乘上增加量 \(\Delta v\)。
\(i^2v_i\) 同理。
注意算出来的权值和要乘 2,因为道路是双向的
点击查看代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
int read()
{
int w{1},x{};
char c= getchar();
while(c>'9'||c<'0'){if(c == '-')w=-1;c=getchar();}
while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+c-'0',c=getchar();
return w * x;
}
void write(int x)
{
if(x<0)x=-x,putchar('-');
if(x>9)write(x/10);
putchar(x%10+'0');
}
void writeln(int x)
{
write(x);putchar(10);
}
const int N = 1e5+5;
int a[N];
struct TREE
{
#define lson(x) x<<1
#define rson(x) x<<1|1
struct NODE
{
int l,r,val,lz;
int val1,val2;
//i*vi vi*i*i
int ii; //表示区间i和
int iiii; //表示区间i^2和
}tree[N<<2];
void pushup(int x)
{
tree[x].val = tree[lson(x)].val+tree[rson(x)].val;
tree[x].val1 = tree[lson(x)].val1+tree[rson(x)].val1;
tree[x].val2 = tree[lson(x)].val2+tree[rson(x)].val2;
tree[x].ii = tree[lson(x)].ii+tree[rson(x)].ii;
tree[x].iiii= tree[lson(x)].iiii+tree[rson(x)].iiii;
}
void build(int l,int r,int x)
{
tree[x].l = l,tree[x].r = r;
int mid = (l+r)>>1;
if(l == r){
tree[x].val = a[l];
tree[x].val1 = l*a[l];
tree[x].val2 = l*l*a[l];
tree[x].ii = l;
tree[x].iiii = l * l;
return ;
}
build(l,mid,lson(x));
build(mid+1,r,rson(x));
pushup(x);
}
void pushdown(int x)
{
if(tree[x].lz)
{//todo delta
int mid = (tree[x].l+tree[x].r)>>1;
tree[lson(x)].lz += tree[x].lz,tree[rson(x)].lz += tree[x].lz;
tree[lson(x)].val += tree[x].lz * (mid-tree[x].l+1);
tree[rson(x)].val += tree[x].lz*(tree[x].r-mid);
tree[lson(x)].val1 += tree[x].lz * tree[lson(x)].ii;
tree[rson(x)].val1 += tree[x].lz * tree[rson(x)].ii;
tree[lson(x)].val2 += tree[x].lz * tree[lson(x)].iiii;
tree[rson(x)].val2 += tree[x].lz * tree[rson(x)].iiii;
tree[x].lz &= 0;
}
}
void update(int l,int r,int k,int x)
{
if(l<=tree[x].l&&tree[x].r<=r)
{//todo
tree[x].lz+=k;tree[x].val += k*(tree[x].r-tree[x].l+1);
tree[x].val1 += k*tree[x].ii;
tree[x].val2 += k*tree[x].iiii;
return;
}
pushdown(x);
int mid= (tree[x].l+tree[x].r)>>1;
if(l<=mid) update(l,r,k,lson(x));
if(r>mid) update(l,r,k,rson(x));
pushup(x);
}
int query(int l,int r,int x)
{
if(l<=tree[x].l&&tree[x].r<=r) return tree[x].val;
pushdown(x);
int mid = (tree[x].l+tree[x].r)>>1;
int res{};
if(l<=mid) res+=query(l,r,lson(x));
if(r>mid) res+=query(l,r,rson(x));
return res;
}
int query1(int l,int r,int x)
{
if(l<=tree[x].l&&tree[x].r<=r) return tree[x].val1;
pushdown(x);
int mid = (tree[x].l+tree[x].r)>>1;
int res{};
if(l<=mid) res+=query1(l,r,lson(x));
if(r>mid) res+=query1(l,r,rson(x));
return res;
}
int query2(int l,int r,int x)
{
if(l<=tree[x].l&&tree[x].r<=r) return tree[x].val2;
pushdown(x);
int mid = (tree[x].l+tree[x].r)>>1;
int res{};
if(l<=mid) res+=query2(l,r,lson(x));
if(r>mid) res+=query2(l,r,rson(x));
return res;
}
}tree;
signed main()
{
#ifdef LOCAL
freopen("in.in","r",stdin);
#endif
cin.tie(0),cout.tie(0);
int n = read(),m = read();
tree.build(1,n,1);//00000
//把r-1统计road
//1-1-1-1
while(m--)
{
char c ;
cin>>c;
if(c == 'C'){int l = read(),r = read(),v = read();tree.update(l,r-1,v,1);}
if(c == 'Q')
{
int l = read(),r = read();
//期望(lr两点之间线段值的期望)
//a-> 1/(r-l+1) b->1/(r-l)
// o 1 o 2 o 2 o
// 1+2+2+3+4+5=34 /12
//1+1+2s+1+2s+2d+2s+2s+2d+2d
int ed = (r-l)*(r-l+1);
//writeln(ed);
int res{};
res = (l+r-1)*tree.query1(l,r-1,1) + (r-l*r)*tree.query(l,r-1,1)-tree.query2(l,r-1,1);
res*=2; //双向道路
int ggccdd = __gcd(res,ed);
res/=ggccdd,ed/=ggccdd;
cout<<res<<"/"<<ed<<(char)10;
}
}
//for(int i{1};i<=n;i++) writeln(tree.query(i,i,1));
return 0;
}

浙公网安备 33010602011771号