bzoj 3148: [Ctsc2013]没头脑和不高兴 数学&线段树

       考虑两个纸牌i和j满足i<j,那么如果a[i]>a[j]则对答案有贡献为1;分四种情况讨论:

       1.i和j都没被排序,期望贡献1/2;2.i和j都被排序,期望贡献0;

       3.只有i被排序,假设被排序的有t个数,那么在期望情况下这t个数是均匀分布的,则a[j]比a[i]小的概率为Pi/(t+1),Pi表示i在t个数中是第几个;

       4.同理只有j被排序时期望为(t-Pi+1)/(t+1);

       这样分4中情况就可算出期望的公式;当N=2n时为(7n^2+n)/12,N=2n+1时为(7n^2-n)/12。

       修改的话考虑用线段树维护区间修改。显然情况1和2是容易维护的;对于所有没有被排序的位置i,如果i前面有k个被排序,而总共有t个被排序的数,那么对于第三种情况答案就是1/(t+1)+2/(t+1)+...+k/(t+1)=(k^2+k)/2(t+1);第四种情况就是1/(t+1)+...+(t-k)/(t+1)=((t-k)^2+t-k)/2(t+1)。注意到分母相同,也就是说我们只需要维护所有i的k^2+k+(t-k)^2+t-k的和即可,拆开来之后可以考虑分别维护k^2和k的和。

       然后还要求方差。。。你可以假设答案次数不是很大然后暴力出几组数据然后拉格朗日差值。。。不过我是直接抄艾神公式的。。N=2n时为(54n^3+13n^2+23n)/360;N=2n+1时为(54n^3+55n^2-29n)/360

AC代码如下:

#include<iostream>
#include<cstdio>
#define ll long long
#define N 300005
using namespace std;

int n,m,tx,ty,tz,tg[N];
struct node{ int s0,s1; ll x,y; }val[N];
ll gcd(ll x,ll y){ return (y)?gcd(y,x%y):x; }
int read(){
	int x=0; char ch=getchar();
	while (ch<'0' || ch>'9') ch=getchar();
	while (ch>='0' && ch<='9'){ x=x*10+ch-'0'; ch=getchar(); }
	return x;
}
void opt(ll x,ll y){
	ll d=gcd(x,y); printf("%lld/%lld\n",x/d,y/d);
}
struct frc{
	ll x,y;
	frc(){}
	frc(ll u,ll v){ ll d=gcd(u,v); x=u/d; y=v/d; }
}ans;
frc operator +(frc u,frc v){
	ll d=gcd(u.y,v.y),fm=u.y/d*v.y;
	return frc(fm/u.y*u.x+fm/v.y*v.x,fm);
}
void maintain(int k){
	int l=k<<1,r=l|1;
	val[k].s0=val[l].s0+val[r].s0; val[k].s1=val[l].s1+val[r].s1;
	val[k].x=val[l].x+val[r].x+(ll)val[l].s1*val[l].s1*val[r].s0+(val[r].y*val[l].s1<<1);
	val[k].y=val[l].y+val[r].y+(ll)val[l].s1*val[r].s0;
}
void cvr(int k,int v){
	if (!v){ val[k].s0+=val[k].s1; val[k].s1=0; }
	else{ val[k].s1+=val[k].s0; val[k].s0=0; }
	val[k].x=val[k].y=0; tg[k]=v;
}
void pushdown(int k){
	if (tg[k]!=-1){
		cvr(k<<1,tg[k]); cvr(k<<1|1,tg[k]); tg[k]=-1;
	}
}
void build(int k,int l,int r){
	tg[k]=-1;
	if (l==r){
		if (l&1) val[k].s1=1; else val[k].s0=1; return;
	}
	int mid=(l+r)>>1;
	build(k<<1,l,mid); build(k<<1|1,mid+1,r);
	maintain(k);
}
void mdy(int k,int l,int r){
	if (tx<=l && r<=ty){
		cvr(k,tz); return;
	}
	pushdown(k); int mid=(l+r)>>1;
	if (tx<=mid) mdy(k<<1,l,mid);
	if (ty>mid) mdy(k<<1|1,mid+1,r);
	maintain(k);
}
int main(){
	n=read(); m=read(); int t=n>>1;
	if (n&1){
		opt(((ll)t*7+1)*t,12);
		opt((((ll)t*54+55)*t-29)*t,360);
	} else{
		opt(((ll)t*7-1)*t,12);
		opt((((ll)t*54+13)*t+23)*t,360);
	}
	build(1,1,n);
	while (m--){
		tx=read(); ty=read(); tz=read();
		mdy(1,1,n);
		ans=frc((ll)val[1].s0*(val[1].s0-1),4);
		ans=ans+frc(((ll)val[1].s1*val[1].s1+val[1].s1)*val[1].s0,(val[1].s1+1)<<1);
		ans=ans+frc((val[1].x-val[1].y*val[1].s1)<<1,(val[1].s1+1)<<1);
		printf("%lld/%lld\n",ans.x,ans.y);
	}
	return 0;
}


by lych

2016.5.25

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值