【题目地址】
- 题意简述
给你一些二元组 ( x , y ) (x,y) (x,y),对于这些二元组的一个排列,如果一个二元组 i i i的左边有 x j ≥ x i x_j\geq x_i xj≥xi,那么这个二元组就会消失。
每种排列的贡献值为其中 y y y的种类数,然后询问你所有排列的总贡献。
暴力枚举排列,然后计算贡献相加,复杂度为 O ( n × n ! ) O(n\times n!) O(n×n!)
- 正解
我们这里考虑对 y y y进行离散化,然后对二元组按照 x x x排序,记录下每一个二元组 x x x大于等于它的个数。
那么对于一种二元组会有贡献的话,我们可以计算出它的贡献的概率,概率就为和它一样的二元组个数除以大于等于它的二元组的个数。(因为大于它的要全部排在至少一种这种二元组后面)
那么由于总的方案数就为 n ! n! n!,所以得知概率后我们就可以知道它有多少种做出贡献的方案。
对于每一种,我们令 k i k_i ki为它做出贡献的概率,那由于一种 y y y可能有多种 x x x,所以我们要反向计算概率贡献,计算式如下:
v a l = n ! × ( 1 − ∏ ( 1 − k i ) ) val=n!\times (1-\prod (1-k_i)) val=n!×(1−∏(1−ki))
其中 k i = c n t t o t k_i=\frac{cnt}{tot} ki=totcnt, c n t cnt cnt为同种二元组的个数, t o t tot tot为大于等于这个二元组的个数。
答案就为 ∑ v a l \sum val ∑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\n",ans%Mod);
return 0;
}