前言
我是照着老師講的+這篇博客思路打的…
某大佬的blog
題目
給出 ,一個隨機的 的排列,現在所有奇數位上有記號,有記號的格子數一開始會自動排序,現在你能從相鄰的逆序對隨機選擇一對交換
問排好序的步數的期望和方差?
增加區間修改操作(修改格子上的記號),每次修改後詢問步數的期望?
思路
發現你選擇交換相鄰逆序對順序對答案無影響,每次操作答案即爲逆序對個數, 是否爲逆序對獨立,於是:
根據期望線性性可得:
考慮對 (i<j) 的計算:
- 均無標記
由於隨機, - 均有標記
- 有, 沒有
似乎有點難,設有 個數打標記, 是第 個數打了標記,我們可以考慮爲在有 個球的盒子裏面抓 個球排好序後隨機指定一個爲 所抓的,得出概率爲 - 有, 沒有
同理 可得概率爲
那麼考慮計算剛開始的期望:
- 長度爲偶數
- 長度爲奇數
(真TM難算)
考慮方差計算:
我們只需要算 即可…
真的不會啊,這 的分佈函數咋求?
好像是利用拉格朗日插值打表
就當這道題練期望吧
方差答案:
長度爲偶數 :
長度爲奇數 :
考慮修改操作,情況 通過簡單的區間操作求出,對於情況 考慮每一個未標記位置 對答案的貢獻:
- , 被標記
設 前面有 個位置被標記,當前總標記爲
那麼 對答案的貢獻爲
然後就是高端操作了…
難點在計算
發現
記標記位置爲 ,沒有爲 ,實際上統計 和 的個數 - , 被標記
差不多,設 後面有 個位置被標記
那麼 對答案的貢獻爲
即統計 和 的個數
線段樹即可 通過統計 個數能求出
代碼
#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個變量教會我做人,這題實際做法跟那道比起來太簡單了…