• 牛客网212D禁书目录Index-题解


    题目地址

    • 题意简述

    给你一些二元组(x,y)(x,y),对于这些二元组的一个排列,如果一个二元组ii的左边有xjxix_jgeq x_i,那么这个二元组就会消失。

    每种排列的贡献值为其中yy的种类数,然后询问你所有排列的总贡献。


    暴力枚举排列,然后计算贡献相加,复杂度为O(n×n!)O(n imes n!)

    • 正解

    我们这里考虑对yy进行离散化,然后对二元组按照xx排序,记录下每一个二元组xx大于等于它的个数。

    那么对于一种二元组会有贡献的话,我们可以计算出它的贡献的概率,概率就为和它一样的二元组个数除以大于等于它的二元组的个数。(因为大于它的要全部排在至少一种这种二元组后面)

    那么由于总的方案数就为n!n!,所以得知概率后我们就可以知道它有多少种做出贡献的方案。

    对于每一种,我们令kik_i为它做出贡献的概率,那由于一种yy可能有多种xx,所以我们要反向计算概率贡献,计算式如下:

    val=n!×(1(1ki))val=n! imes (1-prod (1-k_i))

    其中ki=cnttotk_i=frac{cnt}{tot}cntcnt为同种二元组的个数,tottot为大于等于这个二元组的个数。

    答案就为valsum val

    用概率或者期望来算方案数的题目,方法比较巧妙,是在知道总方案数的时候的一种较为好的计算方式。

    代码简陋的QAQ

    最后线性处理一下阶乘和逆元即可计算取模意义下的答案。

    #include<vector>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define ll long long
    using namespace std;
    const int M=5e5+10,Inf=1e9;
    const ll Mod=998244353ll;
    int ls[M],maxv[M],minv[M],tot,n;
    ll Inv[M],fac;
    struct book{
    	int A,B;
    	void in(){scanf("%d%d",&A,&B);ls[++tot]=B;}
    	book(){}
    	book(int a,int b):A(a),B(b){}
    	bool operator <(const book &a)const{return A<a.A;}
    }Bk[M];
    
    void init(){
    	Inv[1]=1;
    	for(int i=2;i<=n+1;i++)Inv[i]=(Mod-Mod/i)*Inv[Mod%i]%Mod;
    	fac=1;for(int i=2;i<=n;i++)fac=(fac*i)%Mod;
    }
    
    int sze[M],tct;
    struct node{
    	int A,t;
    	node(){}	
    	node(int a,int b):A(a),t(b){}
    	bool operator <(const node &a)const{return A<a.A;}
    };
    vector <node> Type[M];
    vector <ll> rec[M];
    
    int main(){
    	scanf("%d",&n);
    	init();
    	for(int i=1;i<=n;i++)Bk[i].in();
    	sort(ls+1,ls+tot+1);
    	tot=unique(ls+1,ls+tot+1)-ls-1;
    	sort(Bk+1,Bk+n+1);
    	int last=-1,tw=0;
    	for(int i=1;i<=n;i++){
    		int pos=lower_bound(ls+1,ls+tot+1,Bk[i].B)-ls;
    		if(last!=Bk[i].A)last=Bk[i].A,tw=n-i+1;
    		Type[pos].push_back(node(Bk[i].A,tw));
    	}
    	for(int i=tot;i>=1;i--)sort(Type[i].begin(),Type[i].end());
    	for(int i=1;i<=tot;i++){
    		int last=-1,cnt=0,p=0;Type[i].push_back(node(-1,0));
    		for(int j=0,sz=Type[i].size();j<sz;j++){
    			if(last!=Type[i][j].A){
    				if(last!=-1){rec[i].push_back((Mod+1-(cnt*Inv[Type[i][j-1].t]%Mod))%Mod);}
    				last=Type[i][j].A;
    				cnt=1;p=j;
    			}else{++cnt;}
    		}
    	}
    	ll ans=0;
    	for(int i=1;i<=tot;i++){
    		ll now=1;
    		for(int j=0,sz=rec[i].size();j<sz;j++)now=(now*rec[i][j])%Mod;
    		now=(Mod+1-now)%Mod;
    		ans=(ans+fac*now%Mod)%Mod;
    	}
    	printf("%lld
    ",ans%Mod);
    	return 0;
    }
    
  • 相关阅读:
    Python笔记220151023
    B/S和C/S【转载Jane的博客 http://blog.sina.com.cn/liaojane】
    Java Queue
    Java 使用 .this与.new
    Java多态继承与清理
    Java 匿名类
    Java 内部类2
    java 中的多重继承
    Java 内部类
    Java 异常(自定义异常)
  • 原文地址:https://www.cnblogs.com/VictoryCzt/p/10053399.html
Copyright © 2020-2023  润新知