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;
}
posted @ 2024-04-05 17:04  WanGMiNgWeI  阅读(33)  评论(0)    收藏  举报