原文链接https://www.cnblogs.com/zhouzhendong/p/9781060.html
题目传送门 - NowCoder Wannafly 26D
题意
放一放这一题原先的题面:
阿尔法城
空间限制 512MB
时间限制 2s
题目描述
听说遥远的α城里神仙题横行,毒瘤题占道,zzd 决定来送一道温暖。
zzd 现在正距离α城很远处(可以理解成无穷远),通过望远镜看到了 α 城里的景象。
α城中有 n 座高楼,排成一条直线,其中第 i 座高楼的高度为 a[i] ,颜色为 c[i] 。任意两个高楼不同,即使他们的高度和颜色都相同。
zzd 惊喜地发现这些高楼排成的直线恰好与 zzd 的视线重合,且离 zzd 最近的是第 1 座高楼。
如果一座大楼之前没有高度不小于它的高楼遮挡,那么 zzd 就能看见它。于是 zzd 很快就数出了他看到的颜色种数。
α城里的神仙陈老爷想要阻止 zzd 送温暖,立马联合神仙 cly 发动了魔法:不断将高楼重新排列。
这么多的变换,让 zzd 眼花缭乱。于是,zzd 决定,在去找 Mangoyang 并对陈老爷实施 α 行动之前,先问问你:对于所有排列,zzd 看到的颜色种数之和为多少?
答案对 998244353 取模。
输入描述
第一行一个整数 n 。
接下来 n 行,每行两个整数 a[i] 和 c[i] 。
1<=n<=500000,
1<=a[i],c[i]<=100000000
输出描述
一个整数,表示答案对 998244353 取模后的值。
示例
输入1
4
1 5
4 3
5 2
3 1
输出1
50
输入2
10
5 6
1 2
2 2
10 9
10 7
8 6
10 6
1 2
1 1
8 3
输出2
6664320
不过后来由于 ACM 赛制,样例 2 就没了。
题解
首先我们把这个总方案数转化成期望。
根据期望的线性性,总答案可以分解成各个颜色被看见的概率之和。
现在考虑如何求一个颜色被看见的概率。
这里有一个结论——
如果对于一个楼,高度不低于它的(包括它自己)有 $t$ 个,那么它被看见的概率就是 $frac 1t$ 。
(证明:将当前楼插入比他高的楼的空隙中,有 $t$ 个方法,但是只有插在最前面的是能看见的)
那么,一种颜色被看见的概率就是 $1-$ 每一个楼都没被看见的概率。注意到可能存在同种颜色有多个相同高度的楼的情况,需要将上面的结论升级一下——
对于 $k$ 个相同高度的楼,如果高度不低于他们的楼有 $t$ 个,则他们中至少一个被看见的概率就是 $frac kt$
(证明基于之前的结论,推导一下即可)
于是就解决了这个问题。
所以这个通过率是怎么回事……是不是都被某毒瘤出题人的 B 题拦住了?
代码
加了个hash表,成功把时限开到了标程4倍。
#include <bits/stdc++.h> using namespace std; const int N=500005,mod=998244353; int Pow(int x,int y){ int ans=1; for (;y;y>>=1,x=1LL*x*x%mod) if (y&1) ans=1LL*ans*x%mod; return ans; } int read(){ int x=0; char ch=getchar(); while (!isdigit(ch)) ch=getchar(); while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar(); return x; } int n,Fac[N],Inv[N],Ha[N],tax[N],hs=0,tot=0; struct Building{ int a,c; }x[N]; struct hash_map{ static const int Ti=233,mod=1<<19; int cnt,k[mod+1],v[mod+1],nxt[mod+1],fst[mod+1]; int Hash(int x){ unsigned t=x; int v=(t<<3^t)&(mod-1); return v==0?mod:v; } void clear(){ cnt=0; memset(fst,0,sizeof fst); } void update(int x,int a){ int y=Hash(x); for (int p=fst[y];p;p=nxt[p]) if (k[p]==x){ v[p]=a; return; } k[++cnt]=x,nxt[cnt]=fst[y],fst[y]=cnt,v[cnt]=a; return; } int find(int x){ int y=Hash(x); for (int p=fst[y];p;p=nxt[p]) if (k[p]==x) return v[p]; return 0; } int &operator [] (int x){ int y=Hash(x); for (int p=fst[y];p;p=nxt[p]) if (k[p]==x) return v[p]; k[++cnt]=x,nxt[cnt]=fst[y],fst[y]=cnt; return v[cnt]=0; } }Map; vector <int> v[N]; int main(){ //freopen("city9.in","r",stdin); //freopen("city9.out","w",stdout); //int st=clock(); n=read(); for (int i=Fac[0]=1;i<=n;i++){ Fac[i]=1LL*Fac[i-1]*i%mod; v[i].clear(); } Inv[n]=Pow(Fac[n],mod-2); for (int i=n;i>=1;i--) Inv[i-1]=1LL*Inv[i]*i%mod; for (int i=1;i<=n;i++) Inv[i]=1LL*Inv[i]*Fac[i-1]%mod; Map.clear(); for (int i=1;i<=n;i++){ x[i].a=read(),x[i].c=read(); if (!Map[x[i].c]) Map[x[i].c]=++tot; x[i].c=Map[x[i].c]; Ha[++hs]=x[i].a; } sort(Ha+1,Ha+hs+1); hs=unique(Ha+1,Ha+hs+1)-Ha-1; memset(tax,0,sizeof tax); for (int i=1;i<=n;i++){ x[i].a=lower_bound(Ha+1,Ha+hs+1,x[i].a)-Ha; tax[x[i].a]++; v[x[i].c].push_back(x[i].a); } for (int i=hs;i>=1;i--) tax[i]+=tax[i+1]; int ans=0; for (int i=1;i<=tot;i++){ vector <int> &a=v[i]; sort(a.begin(),a.end()); a.push_back(a.back()+1); int h=a[0],cnt=1,now=1; for (int j=1;j<a.size();j++){ if (a[j]!=a[j-1]){ int t=tax[a[j-1]]; now=1LL*now*(mod+1-1LL*cnt*Inv[t]%mod)%mod; cnt=0; } cnt++; } ans=(ans+mod+1-now)%mod; } ans=1LL*ans*Fac[n]%mod; printf("%d",ans); //cerr << "user time = " << clock()-st << endl; return 0; }
数据生成器
里面有参数自行调整。
#include <bits/stdc++.h> using namespace std; typedef unsigned uint; const int N=1000005; int n=500000,m=1000,A[N],C[N]; int Rand(int n){ uint a=rand(),b=rand(); return (a<<16|b)%n; } int main(){ srand(time(NULL)); freopen("city9.in","w",stdout); printf("%d ",n); A[0]=C[0]=1; for (int i=1;i<=n;i++){ int a,c; int p1=Rand(20),p2=Rand(20); if (p1) a=Rand(m)+1; else a=A[Rand(i)]; if (p2) c=Rand(m)+1; else c=C[Rand(i)]; printf("%d %d ",A[i]=a,C[i]=c); } return 0; }