• luogu P2221[HAOI2012]高速公路


    题目大意:有一条$n$个点,$n-1$条边的链。求一个区间$[L,R]$内随机选出两点$a,b$的期望距离。要求支持修改边权。

    看到区间修改和查询,想到线段树。

    考虑一个区间内总距离和:

    因为一条边会被一个在它左侧的点和一个在它右侧的点覆盖,所以总距离和可以表示为:

    $sum_{i=l}^{r} a_i*(r-i+1)*(i-l+1)$

    如果将式子展开,得到$ sum {i=l}{r} a_i*(rl+ri+r+il-i*i+1-l)$

    将后面的式子按i的次数分类

    $sum_{i=l}^{r} a[i]*(rl+1+r-l)+a[i]*(r+l)*i+a[i]*i*i$

    发现后两项在更新时不好计算,所以再额外记录$sum4$记录$sum i$,$sum5$记录$sum i*i$,这样在更新的时候可以方便的用这两个常量计算答案。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define mid (l+r)/2
    #define N 100005
    #define log 40
    #define lowbit(x) x&-x
    #define mod 998244353
    #define int long long
    using namespace std;
    int sum1[400005],sum2[400005],sum3[400005],sum4[400005],sum5[400005],lazy[400005];
    int a[400005];
    int ans1,ans2,ans3;
    int n,m;
    int ls(int x){return x<<1;}
    int rs(int x){return x<<1|1;}
    void calc1(int now)
    {
    	sum4[now]=sum4[ls(now)]+sum4[rs(now)];
    	sum5[now]=sum5[ls(now)]+sum5[rs(now)];
    }
    void calc2(int now)
    {
    	sum1[now]=sum1[ls(now)]+sum1[rs(now)];
    	sum2[now]=sum2[ls(now)]+sum2[rs(now)];
    	sum3[now]=sum3[ls(now)]+sum3[rs(now)];
    }
    void build(int now,int l,int r)
    {
    	if(l==r)
    	{
    		sum4[now]=l;
    		sum5[now]=l*l;
    		return;
    	}
    	build(ls(now),l,mid);
    	build(rs(now),mid+1,r);
    	calc1(now);
    }
    void work(int now,int l,int r,int k)
    {
    	sum1[now]+=k*(r-l+1);
    	sum2[now]+=k*sum4[now];
    	sum3[now]+=k*sum5[now];
    	lazy[now]+=k;
    }
    void update(int now,int l,int r,int ll,int rr,int k)
    {
    	if(ll<=l&&r<=rr)
    	{
    		work(now,l,r,k);
    		return;
    	}
    	if(lazy[now])
    	{
    		work(ls(now),l,mid,lazy[now]);
    		work(rs(now),mid+1,r,lazy[now]);
    		lazy[now]=0;
    	}
    	if(mid>=ll)
    	    update(ls(now),l,mid,ll,rr,k);
    	if(mid<rr)
    	    update(rs(now),mid+1,r,ll,rr,k);
    	calc2(now);
    }
    void query(int now,int l,int r,int ll,int rr)
    {
    	if(ll<=l&&r<=rr)
    	{
    		ans1+=sum1[now];
    		ans2+=sum2[now];
    	    ans3+=sum3[now];
    	    return;
    	}
    	if(lazy[now])
    	{
    		work(ls(now),l,mid,lazy[now]);
    		work(rs(now),mid+1,r,lazy[now]);
    		lazy[now]=0;
    	}
    	if(mid>=ll)
    		query(ls(now),l,mid,ll,rr);
    	if(mid<rr)
    	    query(rs(now),mid+1,r,ll,rr);
    }
    inline int read()
    {
    	int x=0,f=1;
    	char ch=getchar();
    	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    	return x*f;
    }
    int gcd(int a,int b){return(!b)?a:gcd(b,a%b);}
    signed main()
    {
    	scanf("%lld%lld",&n,&m);
    	build(1,1,n);
    	for(int i=1;i<=m;i++)
    	{
    		char ch;
    		cin>>ch;
    		if(ch=='C')
    		{
    			int l,r,v;
    			scanf("%lld%lld%lld",&l,&r,&v);
    			r--;
    			update(1,1,n,l,r,v);
    		}
    		else
    		{
    		    int l,r;
    		    scanf("%lld%lld",&l,&r);
    		    r--;
    		    ans1=ans2=ans3=0;
    		    query(1,1,n,l,r);
    		    int ans=(r-l+1-r*l)*ans1+(r+l)*ans2-ans3;
    		    int q=gcd(ans,(r-l+2)*(r-l+1)/2);
    		    printf("%lld/%lld
    ",ans/q,(r-l+2)*(r-l+1)/2/q);
    		}
    	}
    	return 0;
    }
    

      

  • 相关阅读:
    [转]ABAP动态取得数据
    [转]ABAP学习笔记之三内表
    [转]ABAP实现对变式的修改
    [转]ABAP Search help
    C#中访问私有成员[转载]
    如果在BackgroundWorker运行过程中关闭窗体…
    交叉编译的概念
    索引器的重载的一个例子
    自定义类实现IComparable接口
    ioctl函数
  • 原文地址:https://www.cnblogs.com/handsome-wjc/p/11264509.html
Copyright © 2020-2023  润新知