zoukankan      html  css  js  c++  java
  • @bzoj


    @description@

    没头脑和不高兴是一对形影不离的好朋友,他们一起上学也一起玩耍。

    这天,这对好朋友聚在一起玩纸牌游戏。他们所玩的纸牌总共有 N 张,每一张上面都有一个 1~N 的数字,任意两张纸牌上的数字都不相同。根据他们制定的游戏规则,在每局游戏的开始,所有的牌需要按照从 1~N 的顺序排好。在开心地玩完了一局牌之后,他们发现牌的顺序被弄得乱七八糟,将它们排好序是一件挺麻烦的事情。

    他们将凌乱的纸牌在桌面上排成一排,然后开始了排序工作。不高兴由于在上一局游戏中输了牌,非常不高兴。他只将其中奇数位置的牌排成了升序,然后把剩下的任务推给了没头脑。没头脑非常没头脑,他采取了一个有些笨的排序方式。每次,他找到两张相邻并且顺序不对的牌交换它们,直到整个序列被排好序为止。

    乐于探究的你,想要研究在初始排列随机的情况下没头脑花在交换纸牌上的时间。假设没头脑每交换一对纸牌花费的时间为 1,你希望求出他排序时间的期望。此外,为了更好地分析这个问题,你还希望能够计算出所花时间的方差。更进一步地,如果被不高兴排好序的位置发生了变化,你是否还能求出没头脑用来排序时间的期望呢?

    题目链接。

    @solution@

    那个排序次数显然就是逆序对个数。

    @part 1:期望@

    考虑期望怎么求,可以想到拆解成每一对 (i, j) (i < j) 是逆序对的概率之和。
    分几种情况,考虑这样的概率分别是多少:

    (1)如果 i, j 都在排序的序列中,则显然贡献 0。
    (2)如果 i, j 都不在排序的序列中,应贡献 1/2。
    (3)如果 i, j 一个在一个不在,不妨考虑 i 在 j 不在的情况,另一种情况同理可求。

    假如 i 是序列中的第 p 个元素,序列总长为 m,枚举 i 的值,得到概率为:

    [frac{sum_{k=p}^{n-m+p}{k-1 choose p-1}{n-k choose m-p} imesfrac{k-p}{n-m}}{{nchoose m}} ]

    注意到 (sum_{k=p}^{n-m+p}{k-1 choose p-1}{n-k choose m-p} = {nchoose m}),于是我们就可以进一步化式子:

    [(frac{1}{n-m})(frac{sum_{k=p}^{n-m+p}{k-1 choose p-1}{n-k choose m-p} imes k}{{nchoose m}} - p) ]

    这个 (frac{sum_{k=p}^{n-m+p}{k-1 choose p-1}{n-k choose m-p} imes k}{{nchoose m}}) 其实就是第 p 个元素的期望值。
    一开始我使用的是差分 + 一些简单的组合恒等式来推这个期望值,后来发现有一个更巧妙的方法:用连续期望求离散期望。

    我们不妨在序列的头部加上 0,给序列的尾部加上 n + 1,这样依然满足序列的性质。
    那么相当于从线段 [0, n + 1] 中随机选择 m 个点,划分成 m + 1 个段,第 p 个点的值为前 p 段之和。
    那么根据连续期望的常识,这个值 = (p imes frac{n+1}{m+1})。和另一种方法的答案一样。

    综上,求出来的贡献为 (frac{p}{n-m} imes(frac{n+1}{m+1} - 1) = frac{p}{m+1})
    可以统计 (k, i, j) 的个数,满足 k <= i < j 且 i, k 在序列中。最后用个数 / (m + 1) 就是要求的逆序对期望。
    用个线段树即可。

    @part 2:方差@

    至于方差怎么求。。。本来我是想手算的,结果算到一半实在算不下去了,于是查看了一下题解。
    结果。。。“大胆猜测方差的表达式是个次数不高的多项式,拉格朗日插值即可。”
    【缓缓打出一个问号.jpg】

    分类 N 为偶数或奇数,求出来是一个关于 N 的 3 次多项式:

    [frac{27N^3 - 26N^2 - 87N + 86}{1440} (odd)\ frac{27N^3 + 13N^2 + 46N}{1440} (even)]

    @accepted code@

    #include <cstdio>
    #include <iostream>
    using namespace std;
    
    typedef long long ll;
    
    const int MAXN = 100000;
    
    ll gcd(ll x, ll y) {
    	return y == 0 ? x : gcd(y, x % y);
    }
    
    int N, M;
    
    struct segtree{
    	#define lch (x << 1)
    	#define rch (x << 1 | 1)
    	typedef pair<int, int> pii;
    	struct node{
    		int le, ri;
    		int cnt0, cnt1, tag;
    		ll s, sl, sr, sum;
    	}t[4*MAXN + 5];
    	void build(int x, int l, int r) {
    		t[x].le = l, t[x].ri = r;
    		t[x].cnt0 = r - l + 1, t[x].tag = -1;
    		if( l == r ) return ;
    		int m = (l + r) >> 1;
    		build(lch, l, m), build(rch, m + 1, r);
    	}
    	void maintain(int x, int v) {
    		if( v == 0 ) {
    			t[x].cnt0 = t[x].ri - t[x].le + 1, t[x].cnt1 = 0;
    			t[x].sl = t[x].sr = t[x].sum = 0;
    			t[x].s = 0, t[x].tag = v;
    		}
    		else {
    			t[x].cnt1 = t[x].ri - t[x].le + 1, t[x].cnt0 = 0;
    			t[x].sl = t[x].sr = t[x].sum = 0;
    			t[x].s = 1LL*(t[x].ri - t[x].le + 2)*(t[x].ri - t[x].le + 1)/2, t[x].tag = v;
    		}
    	}
    	void pushdown(int x) {
    		if( t[x].tag != -1 ) {
    			maintain(lch, t[x].tag);
    			maintain(rch, t[x].tag);
    			t[x].tag = -1;
    		}
    	}
    	void pushup(int x) {
    		t[x].cnt0 = t[lch].cnt0 + t[rch].cnt0;
    		t[x].cnt1 = t[lch].cnt1 + t[rch].cnt1;
    		t[x].s = t[lch].s + t[rch].s + 1LL*t[lch].cnt1*t[rch].cnt1;
    		t[x].sl = t[lch].sl + t[rch].sl + 1LL*t[lch].cnt1*t[rch].cnt0;
    		t[x].sr = t[lch].sr + t[rch].sr + 1LL*t[lch].cnt0*t[rch].cnt1;
    		t[x].sum = t[lch].sum + t[rch].sum;
    		t[x].sum += 1LL*t[lch].cnt1*t[rch].sl + 1LL*t[lch].s*t[rch].cnt0;
    		t[x].sum += 1LL*t[rch].cnt1*t[lch].sr + 1LL*t[rch].s*t[lch].cnt0;
    	}
    	void change(int x, int ql, int qr, int v) {
    		if( ql > t[x].ri || qr < t[x].le )
    			return ;
    		if( ql <= t[x].le && t[x].ri <= qr ) {
    			maintain(x, v);
    			return ;
    		}
    		pushdown(x);
    		change(lch, ql, qr, v);
    		change(rch, ql, qr, v);
    		pushup(x);
    	}
    }T;
    
    void print(ll x, ll y) {
    	ll d = gcd(x, y); x /= d, y /= d;
    	printf("%lld/%lld
    ", x, y);
    }
    void get1() {
    	ll K = T.t[1].cnt1, S = T.t[1].sum;
    	ll x1 = S*4, x2 = (N-K)*(N-K-1)*(K+1), y = 4*(K+1);
    	print(x1 + x2, y);
    }
    void get2() {
    	ll x = N;
    	if( x & 1 )
    		print(((27*x - 26)*x - 87)*x + 86, 1440);
    	else print(((27*x + 13)*x + 46)*x, 1440);
    }
    
    int main() {
    	scanf("%d%d", &N, &M), T.build(1, 1, N);
    	for(int i=1;i<=N;i+=2) T.change(1, i, i, 1);
    	get1(), get2();
    	for(int i=1;i<=M;i++) {
    		int l, r, v;
    		scanf("%d%d%d", &l, &r, &v);
    		T.change(1, l, r, v), get1();
    	}
    }
    

    @details@

    注意一下开 long long,其他没了。

  • 相关阅读:
    将Color的格式转变成颜色值
    F# 学习笔记 1 基础学习
    GridView 72般绝技
    前台直接读取Web.config中的值的方法
    根据属性名称 获取对象的属性值
    字符集与字符编码简介(转)
    一个WinForm程序配置信息的简单模型和维护工具——设计说明
    扩展DLL调用扩展DLL的LINK2001错误的解决办法之一
    CProfile : 读写私有INI配置文件
    日记:如何在MFC中使用Winsock2
  • 原文地址:https://www.cnblogs.com/Tiw-Air-OAO/p/12179446.html
Copyright © 2011-2022 走看看