zoukankan      html  css  js  c++  java
  • [Nowcoder212D]禁书目录_概率期望

    禁书目录

    题目大意清教需要定期给Index清除记忆,在此之前需要把当中的十万三千本禁书取出来......不幸的是,禁书一旦离开了Index就非常脆弱,具体来说,每一本禁书都有一个魔力值 ai ,其记载的内容是 bi ,取出后的 n 本不同的禁书形成了一个排列,如果说对于一本禁书 i ,其左边存在一本不弱于它的魔力值的禁书 j ,禁书 i 就会因为禁书 j 的影响而消失。求对于所有可能的禁书排列,能保留下来的记载内容的种类数之和。由于答案可能很大,只需要输出对998244353 取膜后的结果即可。

    数据范围:1 ≤ n ≤ 5 x 105, 1≤ ai, bi ≤ 108


    题解

    这题好啊

    第一道数数题转成概率期望的。

    主要是,这个题数数没法数,想起来过于憋尿,我们把它变成概率期望。

    假设$a$值不小于当前$a$的书有$cnt$本,那么这本书活下来的概率就是$frac{1}{cnt}$。

    所以对于每一种书,算出来全都活不下来的概率,最后乘上阶乘就好了。

    需要注意的是,如果出现了两本书完全相同,那么我们就只能统计其中一本因为在后面的书无论如何都会被前面的干掉。

    代码

    #include <bits/stdc++.h>
    
    #define N 500010 
    
    using namespace std;
    
    typedef long long ll;
    
    const int mod = 998244353 ; 
    
    map<int, int> MP;
    
    struct Node {
    	int x, y;
    }a[N], b[N];
    
    inline bool cmp(const Node &a, const Node &b) {
    	return a.x == b.x ? a.y < b.y : a.x < b.x;
    }
    inline bool operator == (const Node &a, const Node &b) {
    	return a.x == b.x && a.y == b.y;
    }
    
    char *p1, *p2, buf[100000];
    
    #define nc() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2) ? EOF : *p1 ++ )
    
    int rd() {
    	int x = 0;
    	char c = nc();
    	while (c < 48) {
    		c = nc();
    	}
    	while (c > 47) {
    		x = (((x << 2) + x) << 1) + (c ^ 48), c = nc();
    	}
    	return x;
    }
    
    int qpow(int x, int y) {
    	int ans = 1;
    	while (y) {
    		if (y & 1) {
    			ans = (ll)ans * x % mod;
    		}
    		y >>= 1;
    		x = (ll)x * x % mod;
    	}
    	return ans;
    }
    
    int c[N << 1], cnt, re[N << 1];
    
    int main() {
    	int n = rd();
    	int sum = 1;
    	for (int i = 1; i <= n; i ++ ) {
    		sum = (ll)sum * i % mod;
    	}
    	for (int i = 1; i <= n; i ++ ) {
    		a[i].x = rd(), a[i].y = rd();
    		c[ ++ cnt] = a[i].x, c[ ++ cnt] = a[i].y;
    	}
    	int tot = 0;
    	sort(c + 1, c + cnt + 1);
    	c[0] = c[1] - 1;
    	for (int i = 1; i <= cnt; i ++ ) {
    		if (c[i] != c[i - 1]) {
    			MP[c[i]] = ++tot;
    		}
    	}
    	for (int i = 1; i <= n; i ++ ) {
    		a[i].x = MP[a[i].x], a[i].y = MP[a[i].y];
    	}
    
    	sort(a + 1, a + n + 1, cmp);
    	int pre = 0;
    	for (int i = 1; i <= tot; i ++ ) {
    		re[i] = 1;
    	}
    	for (int i = 1; i <= n; i ++ ) {
    		int dic = i;
    		while (dic < n && a[dic] == a[dic + 1]) {
    			dic ++ ;
    		}
    		if (a[i].x != a[pre].x) {
    			pre = i;
    		}
    		int mdl = n - pre + 1;
    		re[a[dic].y] = (ll)re[a[dic].y] * (mdl - (dic - i + 1)) % mod * qpow(mdl, mod - 2) % mod;
    		i = dic;
    	}
    	int ans = 0;
    	for (int i = 1; i <= tot; i ++ ) {
    		ans = (ans + (1 - re[i] + mod) % mod) % mod;
    	}
    	cout << (ll)ans * sum % mod << endl ;
    	return 0;
    }
    

    小结:好题啊,这个题感觉很有意义的。就是如果碰见了一道数数题完全没思路(及时模数很有诱惑力),我们可以考虑转化成概率期望来做。

  • 相关阅读:
    刷新页面的问题
    JS属性
    1-4_基本概念_进程间通信及同步
    1-3_基本概念_程序&进程
    1-2_基本概念_用户和组&目录、链接及文件
    1-1_基本概念_内核&shell
    d 操作系统概述
    对比生成测试算法用例
    简单的排序算法:冒泡排序法
    简单的排序算法:插入排序法
  • 原文地址:https://www.cnblogs.com/ShuraK/p/11256762.html
Copyright © 2011-2022 走看看