题目描述
Y901高速公路是一条重要的交通纽带,政府部门建设初期的投入以及使用期间的养护费用都不低,因此政府在这条高速公路上设立了许多收费站。 Y901高速公路是一条由N-1段路以及N个收费站组成的东西向的链,我们按照由西向东的顺序将收费站依次编号为1~N,从收费站i行驶到i+1(或从i+1行驶到i)需要收取Vi的费用。高速路刚建成时所有的路段都是免费的。 政府部门根据实际情况,会不定期地对连续路段的收费标准进行调整,根据政策涨价或降价。 无聊的小A同学总喜欢研究一些稀奇古怪的问题,他开车在这条高速路上行驶时想到了这样一个问题:对于给定的l,r(l<r),在第l个到第r个收费站里等概率随机取出两个不同的收费站a和b,那么从a行驶到b将期望花费多少费用呢?
输入格式
第一行2个正整数N,M,表示有N个收费站,M次调整或询问 接下来M行,每行将出现以下两种形式中的一种 C l r v 表示将第l个收费站到第r个收费站之间的所有道路的通行费全部增加v Q l r 表示对于给定的l,r,要求回答小A的问题 所有C与Q操作中保证1<=l<r<=N
输出格式
对于每次询问操作回答一行,输出一个既约分数 若答案为整数a,输出a/1
样例
样例输入
4 5
C 1 4 2
C 1 2 -1
Q 1 2
Q 2 4
Q 1 4
样例输出
1/1
8/3
17/6
数据范围与提示
所有C操作中的v的绝对值不超过10000
在任何时刻任意道路的费用均为不超过10000的非负整数
所有测试点的详细情况如下表所示
Test N M
1 =10 =10
2 =100 =100
3 =1000 =1000
4 =10000 =10000
5 =50000 =50000
6 =60000 =60000
7 =70000 =70000
8 =80000 =80000
9 =90000 =90000
10 =100000 =100000
首先这个题一看就是一道数据结构题,废话,考虑一下这个题要求什么,一般用分数表示的概率期望都是假的,不要问我为什么。。这道题其实就是要找一个子区间的和,再除以选端点的方案数$C_{len}^{2}$,那么好像暴力只能用$O(n^3)$打,毕竟枚举子区间就要$n^2$的。
我们现在要考虑暴力如何优化,这道题和HAOI的另一道题树上染色的思路很像,那个题中两点距离通过找边的贡献得到,进而把枚举两点变做加边贡献,那么这个题也从这个思路考虑,枚举每一段路的贡献,那么我们就的到了$O(n)$修改/查询的式子:
$\sum \limits_{i=l}^{r} s[i]*(r-i+1)*(i-l+1)$
这个使我们成功转化思路,这样的优化暴力能水过90分,已经非常优秀了。如果要A过,我们考虑用线段树去维护这个东西,我们首先的对这个式子变形,目的是得到可以区间合并的并且log查询的东西。我们先把括号都拆掉。
$\sum \limits_{i=l}^{r} s[i]*i*(l+r)-s[i]*i^{2}-s[i]*(l-1)*(r+1)$
然后把常数提出来,能一起维护的放在一起。
$(l+r)*\sum \limits_{i=l}^{r} s[i]*i-\sum \limits_{i=l}^{r}s[i]*i^{2}-(l-1)(r+1)*\sum \limits_{i=l}^{r}s[i]$
那么我们已经发现,此时我们对于一个区间,只需要维护它的$s[i]$,$s[i]*i^{2}$,$s[i]*i$就可以了。
查询已经解决,那么如何修改呢,$s[i]$,$s[i]*i$都比较简单,一个是加$w*1$,一个是$s[i]*i+w*i$等差数列即可,最后是$s[i]*i^{2}+w*i^{2}$,这个平方就要用到自然数方幂和公式,借此机会习得拉格朗日插值。当然只要知道公式就可以了$n(n+1)(2n+1)/6$。
友情提示,懒标记是用来叠加的,不是用来覆盖的。。。。
#include<iostream> #include<cstdio> #define ll long long #define int long long using namespace std; const int N=1000020,M=100020; struct node { ll s,si,sii; node operator +(node b) { node c;c.s=c.si=c.sii=0; c.s=s+b.s; c.si=si+b.si; c.sii=sii+b.sii; return c; } }; struct tree{int l,r;ll f;node s;}tr[4*N]; int rd() { int s=0,w=1; char cc=getchar(); while(cc<'0'||cc>'9') {if(cc=='-') w=-1;cc=getchar();} while(cc>='0'&&cc<='9') s=(s<<3)+(s<<1)+cc-'0',cc=getchar(); return s*w; } void change(int k,int w) { int l=tr[k].l,r=tr[k].r; tr[k].s.s+=1ll*w*(r-l+1); tr[k].s.si+=1ll*w*((l+r)*(r-l+1)/2); tr[k].s.sii+=1ll*w*((r*(r+1)*(2*r+1)-(l-1)*l*(2*l-1))/6); tr[k].f+=w; } void down(int k) { change((k<<1),tr[k].f); change((k<<1|1),tr[k].f); tr[k].f=0; } void build(int k,int l,int r) { tr[k].l=l;tr[k].r=r; if(l==r) { tr[k].f=tr[k].s.si=tr[k].s.sii=tr[k].s.s=0; return ; } int mid=(l+r)>>1; build(k<<1,l,mid);build(k<<1|1,mid+1,r); tr[k].s=tr[k<<1].s+tr[k<<1|1].s; } node ask(int k,int x,int y) { //cout<<tr[k].l<<" "<<tr[k].r<<" "<<x<<" "<<y<<" "<<tr[k].s.s<<endl; int l=tr[k].l,r=tr[k].r,mid=(l+r)>>1; if(tr[k].f) down(k); if(x==l&&r==y) return tr[k].s; if(y<=mid) return ask(k<<1,x,y); else if(x>mid) return ask(k<<1|1,x,y); return ask(k<<1,x,mid)+ask(k<<1|1,mid+1,y); } void add(int k,int x,int y,int w) { //cout<<k<<" "<<x<<" "<<y<<" "<<tr[k].s.s<<endl; int l=tr[k].l,r=tr[k].r,mid=(l+r)>>1; if(tr[k].f) down(k); if(x==l&&r==y) { change(k,w); return; } if(y<=mid) add(k<<1,x,y,w); else if(x>mid) add(k<<1|1,x,y,w); else add(k<<1,x,mid,w),add(k<<1|1,mid+1,y,w); tr[k].s=tr[k<<1].s+tr[k<<1|1].s; } ll gcd(ll a,ll b){return b?gcd(b,a%b):a;} signed main() { //freopen("data.in","r",stdin); //freopen("data.out","w",stdout); int n=rd()-1,m=rd();ll ans=0,len,d; char op[2]; build(1,1,n); for(int i=1,x,y,z;i<=m;i++) { scanf("%s",op); if(op[0]=='C') { x=rd(),y=rd()-1,z=rd(); //cout<<i<<" "<<x<<" "<<y<<endl; add(1,x,y,z); // cout<<ask(1,31,39).s<<endl; continue; } if(op[0]=='Q') { x=rd(),y=rd()-1; // cout<<x<<" "<<y<<endl; node tmp=ask(1,x,y); ans=(tmp.si*(x+y)-tmp.sii-1ll*(x-1)*(y+1)*tmp.s); //cout<<ask(1,1,2).s<<endl; //cout<<tmp.s<<" "<<tmp.si*(x+y)<<" "<<tmp.sii<<" "<<(x-1)*(y+1)*tmp.s<<endl; len=1ll*(y-x+2)*(y-x+1)/2; if(ans==0) puts("0/1"); else d=gcd(ans,len),printf("%lld/%lld\n",ans/d,len/d); } } } /* g++ 1.cpp -o 1 ./1 4 5 C 1 4 2 C 1 2 -1 Q 1 2 Q 2 4 Q 1 4 */