Wannafly挑战赛26 D.禁书目录 排列组合 贡献

9 篇文章 0 订阅
7 篇文章 0 订阅

链接:https://www.nowcoder.com/acm/contest/212/D
来源:牛客网
 

题目描述

清教需要定期给Index清除记忆,在此之前需要把当中的十万三千本禁书取出来......

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

输入描述:

第一行一个数 n。
接下来 n 行,第 i 行两个整数 ai, bi ,表示第 i 本禁书的魔力值和记载内容。

输出描述:

输出共一个数,表示答案。

题解:

一本书会失效, 那么大于等于他魔法值的全在他后面,这样的情况是所有情况 * 1 / 大于等于他的数量。

为什么?证明: 小于他魔法值的忽略不看,情况就是剩余元素中他排最前面的情况数, 概率是 1 / 数量。

假如种类不会重复, 直接这么算贡献即可。

现在要去重, 把所有种类相同的书放在同一个vector 里面,按魔法值从小到大排序。

然后对于当前魔法值,他能产生贡献必须是他前面的书没有产生贡献的情况里面选,我们只需用总情况减去前面产生贡献了的情况,然后再乘概率即可。

代码:

#include <bits/stdc++.h>
#ifdef LOCAL
#define debug(x) cout<<#x<<" = "<<(x)<<endl;
#else
#define debug(x) 1;
#endif

#define chmax(x,y) x=max(x,y)
#define chmin(x,y) x=min(x,y)
#define lson id<<1,l,mid
#define rson id<<1|1,mid+1,r
#define lowbit(x) x&-x
#define mp make_pair
#define pb push_back
#define fir first
#define sec second
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<ll, int> pii;

const int MOD = 998244353 ;
const double PI = acos (-1.);
const double eps = 1e-10;
const int INF = 0x3f3f3f3f;
const ll INFLL = 0x3f3f3f3f3f3f3f3f;
const int MAXN = 5e5 + 5;

map<int, vector<int> > v;
int a[MAXN], b[MAXN];

ll fastpow (int a, int n) {
    ll ret = 1, base = a;
    while(n) {
        if (n & 1) ret = ret * base % MOD;
        base = base * base % MOD;
        n >>= 1;
    }
    return ret;
}

ll fac[MAXN];

int main() {
#ifdef LOCAL
    freopen ("input.txt", "r", stdin);
#endif
    int n;
    scanf ("%d", &n);
    fac[0] = 1;
    for (int i = 1; i <= n; i++) fac[i] = fac[i - 1] * i % MOD;
    vector<int> all;
    for (int i = 1; i <= n; i++) {
        scanf ("%d %d", &a[i], &b[i]);
        v[b[i]].pb (a[i]);
        all.pb(a[i]);
    }

    sort(all.begin(), all.end());
    ll ans = 0;
    for (auto p : v) {
        sort(p.second.begin(), p.second.end());
        vector<int> & ar = p.second;
        ll pre = 0;
        int cnt = 0, pn = -1;
        for (int i = 0; i < ar.size(); i++) {
            if (pn == ar[i]) cnt++;
            else cnt = 0;
            pn = ar[i];
            int bigger = n - (lower_bound(all.begin(), all.end(), ar[i]) - all.begin()) - cnt;
           // debug(bigger)
            ll t = (fac[n] - pre + MOD) % MOD * fastpow(bigger, MOD - 2) % MOD;
            pre = (pre + t) % MOD;
            ans = (ans + t) % MOD;
        }
    }
    printf ("%lld\n", ans);
    return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值