沒頭腦和不高興[CTSC2013][期望+線段樹]

前言

我是照着老師講的+這篇博客思路打的…
某大佬的blog

題目

給出 n,mn,m ,一個隨機的 1n1\sim n 的排列,現在所有奇數位上有記號,有記號的格子數一開始會自動排序,現在你能從相鄰的逆序對隨機選擇一對交換
Q1:Q1:問排好序的步數的期望和方差?
Q2:Q2:增加區間修改操作(修改格子上的記號),每次修改後詢問步數的期望?
1n,m1051\le n,m\le 10^5

思路

發現你選擇交換相鄰逆序對順序對答案無影響,每次操作答案即爲逆序對個數,(i,j)(i,j) 是否爲逆序對獨立,於是:
根據期望線性性可得:

E()=E()=i=1nj=i+1npijE(步數)=E(逆序對個數)=\sum_{i=1}^n\sum_{j=i+1}^np_{ij}

考慮對 pijp_{ij} (i<j) 的計算:

  1. i,ji,j 均無標記
    由於隨機,pij=12p_{ij}=\frac{1}{2}
  2. i,ji,j 均有標記
    pij=0p_{ij}=0
  3. ii 有, jj 沒有
    似乎有點難,設有 xx 個數打標記, ii 是第 rnkirnk_i 個數打了標記,我們可以考慮爲在有 nn 個球的盒子裏面抓 x+1x+1 個球排好序後隨機指定一個爲 jj 所抓的,得出概率爲 rnkix+1\frac{rnk_i}{x+1}
  4. jj 有, ii 沒有
    同理 33 可得概率爲 x+1rnkix+1\frac{x+1-rnk_i}{x+1}

那麼考慮計算剛開始的期望:

  1. 長度爲偶數 2n2n
    E(X)=12(n2)+(1nn+1+2(n1)n+1+...+n1n+1)+((n+12)1n+1+(n+13)2n+1+...+(n+1n)(n1)n+1E(X)=\frac{1}{2}\binom{n}{2}+(\frac{1*n}{n+1}+\frac{2*(n-1)}{n+1}+...+\frac{n*1}{n+1})+(\frac{(n+1-2)*1}{n+1}+\frac{(n+1-3)*2}{n+1}+...+\frac{(n+1-n)*(n-1)}{n+1}
    =n(n1)4+i=1ni(ni+1)n+1+i=1n1(ni)in+1=\frac{n*(n-1)}{4}+\sum_{i=1}^{n}\frac{i*(n-i+1)}{n+1}+\sum_{i=1}^{n-1}\frac{(n-i)*i}{n+1}
    =n(n1)4+1n+1((n+1)2n2n(n+1)(2n+1)6+(n1)n22(n1)n(2n1)6)=\frac{n*(n-1)}{4}+\frac{1}{n+1}(\frac{(n+1)^2*n}{2}-\frac{n*(n+1)*(2n+1)}{6}+\frac{(n-1)*n^2}{2}-\frac{(n-1)*n*(2n-1)}{6})
    =7n2n12=\frac{7n^2-n}{12}
  2. 長度爲奇數 2n+12n+1
    E(X)=12(n2)+(1nn+2+2(n1)n+2+...+1nn+2)+((n+22)1n+2+(n+23)2n+2+...+(n+2(n+1))nn+2E(X)=\frac{1}{2}\binom{n}{2}+(\frac{1*n}{n+2}+\frac{2*(n-1)}{n+2}+...+\frac{1*n}{n+2})+(\frac{(n+2-2)*1}{n+2}+\frac{(n+2-3)*2}{n+2}+...+\frac{(n+2-(n+1))*n}{n+2}
    =7n2+n12=\frac{7n^2+n}{12}

(真TM難算)
考慮方差計算:
D((xE(x))2)=E(x22xE(x)+E(x)2)D((x-E(x))^2)=E(x^2-2xE(x)+E(x)^2)
=E(x2)2E(xE(x))+E(x)2=E(x^2)-2E(xE(x))+E(x)^2
=E(x2)E(x)2=E(x^2)-E(x)^2
我們只需要算 E(x2)E(x^2) 即可…
真的不會啊,這 xx 的分佈函數咋求?
好像是利用拉格朗日插值打表
就當這道題練期望吧
方差答案:

長度爲偶數 2n2n : 54n3+13n2+23n360\frac{54*n^3+13*n^2+23n}{360}

長度爲奇數 2n+12n+1 : 54n3+55n229n360\frac{54*n^3+55*n^2-29n}{360}

考慮修改操作,情況 1,21,2 通過簡單的區間操作求出,對於情況 3,43,4 考慮每一個未標記位置 ii 對答案的貢獻:

  1. j<ij<ijj 被標記
    ii 前面有 kk 個位置被標記,當前總標記爲 xx
    那麼 ii 對答案的貢獻爲 i=1kix+1=1x+1k(k+1)2\sum_{i=1}^k\frac{i}{x+1}=\frac{1}{x+1}*\frac{k*(k+1)}{2}
    然後就是高端操作了…
    難點在計算 k(k+1)2\frac{k*(k+1)}{2}
    發現 k(k+1)2=k(k1)2+k=(k2)+k\frac{k*(k+1)}{2}=\frac{k*(k-1)}{2}+k=\binom{k}{2}+k
    記標記位置爲 11 ,沒有爲 00 ,實際上統計 1101101010 的個數
  2. i<ji<jjj 被標記
    差不多,設 ii 後面有 kk 個位置被標記
    那麼 ii 對答案的貢獻爲 i=x+1kxx+1ix+1=i=1kix+1=1x+1k(k+1)2\sum_{i=x+1-k}^x\frac{x+1-i}{x+1}=\sum_{i=1}^k\frac{i}{x+1}=\frac{1}{x+1}*\frac{k*(k+1)}{2}
    即統計 0110110101 的個數

線段樹即可 1,21,2 通過統計 0,10,1 個數能求出

代碼

#include<map>
#include<set>
#include<stack>
#include<queue>
#include<cmath>
#include<cstring>
#include<climits>
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
#define LL long long
//#define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++)
//char buf[(1 << 21) + 1], *p1 = buf, *p2 = buf;
inline int read() {
	bool f=0;int x=0;char c=getchar();
	while(c<'0'||'9'<c){if(c==EOF)exit(0);if(c=='-')f=1;c=getchar();}
	while('0'<=c&&c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
	return !f?x:-x;
}
#define MAXN 100000
#define INF 0x3f3f3f3f
LL gcd(LL a,LL b){return !b?a:gcd(b,a%b);}
struct node{
	int l,r,lazy;
	LL cnt_0,cnt_1,cnt_01,cnt_10,cnt_011,cnt_110;
	friend node operator + (node a,node b){
		node c;
		c.l=a.l,c.r=b.r,c.lazy=-1;
		c.cnt_0=a.cnt_0+b.cnt_0;
		c.cnt_1=a.cnt_1+b.cnt_1;
		c.cnt_01=a.cnt_01+b.cnt_01+a.cnt_0*b.cnt_1;
		c.cnt_10=a.cnt_10+b.cnt_10+a.cnt_1*b.cnt_0;
		c.cnt_011=a.cnt_011+b.cnt_011+a.cnt_01*b.cnt_1+a.cnt_0*b.cnt_1*(b.cnt_1-1)/2;
		c.cnt_110=a.cnt_110+b.cnt_110+a.cnt_1*b.cnt_10+a.cnt_1*(a.cnt_1-1)/2*b.cnt_0;
		return c;
	}
}tree[5*MAXN+5];
#define lch (u<<1)
#define rch (u<<1|1)
void PushUp(int u){
	tree[u]=tree[lch]+tree[rch];
	return ;
}
void PushDown(int u,int L,int Mid,int R){
	if(tree[u].lazy==-1) return ;
	tree[lch]=(node){L,Mid,tree[u].lazy,(Mid-L+1)*(tree[u].lazy^1),(Mid-L+1)*tree[u].lazy,0,0,0,0};
	tree[rch]=(node){Mid+1,R,tree[u].lazy,(R-Mid)*(tree[u].lazy^1),(R-Mid)*tree[u].lazy,0,0,0,0};
	tree[u].lazy=-1;
	return ;
}
void Build(int u,int L,int R){
	if(L==R){
		tree[u]=(node){L,R,-1,(L&1)^1,(L&1),0,0,0,0};
		return ;
	}
	int Mid=(L+R)>>1;
	Build(lch,L,Mid),Build(rch,Mid+1,R);
	PushUp(u);
	return ;
}
void Modify(int u,int L,int R,int qL,int qR,int v){
	if(qL<=L&&R<=qR){
		tree[u]=(node){L,R,v,(R-L+1)*(v^1),(R-L+1)*v,0,0,0,0};
		return ;
	}
	int Mid=(L+R)>>1;
	PushDown(u,L,Mid,R);
	if(qL<=Mid)
		Modify(lch,L,Mid,qL,qR,v);
	if(Mid+1<=qR)
		Modify(rch,Mid+1,R,qL,qR,v);
	PushUp(u);
	return ;
}
int main(){
	LL n=read(),x=n/2,m=read();
	if(n&1){
		LL p=7*x*x+x,q=12,g=gcd(p,q);
		printf("%lld/%lld\n",p/g,q/g);
		p=54*x*x*x+55*x*x-29*x,q=360,g=gcd(p,q);
		printf("%lld/%lld\n",p/g,q/g);
	}
	else{
		LL p=7*x*x-x,q=12,g=gcd(p,q);
		printf("%lld/%lld\n",p/g,q/g);
		p=54*x*x*x+13*x*x+23*x,q=360,g=gcd(p,q);
		printf("%lld/%lld\n",p/g,q/g);
	}
	Build(1,1,n);
	for(int i=1;i<=m;i++){
		int l=read(),r=read(),v=read();
		Modify(1,1,n,l,r,v);
		node Rt=tree[1];
		LL p1=Rt.cnt_01+Rt.cnt_011+Rt.cnt_10+Rt.cnt_110,q1=Rt.cnt_1+1;
		LL p2=Rt.cnt_0*(Rt.cnt_0-1),q2=4;
		LL p=p1*q2+p2*q1,q=q1*q2,g=gcd(p,q);
		printf("%lld/%lld\n",p/g,q/g);
	}
	return 0;
}

題外話

CF的k不相交子段和的18個變量教會我做人,這題實際做法跟那道比起來太簡單了…

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章