传送门
Description
给 (n) 个二维点 ((a_i,b_i)),询问有多少种排列 (p)(答案对 (10^9+7) 取模)使得执行以下伪代码后留下的点是 (i),即最后 ( ext{saved} = i)。
saved = p[1] for x from 2 to n if a[p[x]] >= a[saved] and b[p[x]] >= b[saved] saved = p[x]
保证 (a) 和 (b) 分别为一个排列。
Solution
对于一个点,如果不存在两维都比他大的点,那么它有解
考虑(saved)总共存储过的值,设为(A_1,...,A_k)
那么,在所有横纵坐标都比(A_i)的大的点中,(A_{i+1})一定是最早出现的
设(num[i])表示有(num[i])个点一定不能排在(i)之前((i)自己也算),特别的(num[p_1]=n)
所以这样的排列(指依次停留在(A_1,...,A_k))会有(frac{n!}{prod_{i=1}^k num[A_i]})
简化为(frac{(n-1)!}{prod_{i=1}^{k-1}b[A_i]}),(b[i])表示横纵坐标都比(i)号点大的点的个数
可以(dp)求解,需要做个二维求和,可以先对一维排序,第二维用树状数组维护即可
Code
#include<bits/stdc++.h>
#define ll long long
#define dbg1(x) cerr<<#x<<"="<<(x)<<" "
#define dbg2(x) cerr<<#x<<"="<<(x)<<"
"
#define dbg3(x) cerr<<#x<<"
"
using namespace std;
#define reg register
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
return x*f;
}
const int MN=1e5+5,P=1e9+7;
int Mul(int x,int y){return (1ll*x*y)%P;}
int Add(int x,int y){return (x+y)%P;}
int N,inv[MN];
struct d{int x,y,id;}a[MN];
int T[MN],b[MN],ans[MN];
void C(int x,int y){for(;x<=N;x+=(x&-x))T[x]=Add(T[x],y);}
int G(int x){int r=0;for(;x;x-=(x&-x))r=Add(r,T[x]);return r;}
bool cmp(d x,d y){return (x.x!=y.x)?(x.x<y.x):(x.y<y.y);}
int main()
{
N=read();reg int i;
for(inv[0]=inv[1]=1,i=2;i<=N;++i)inv[i]=Mul(inv[P%i],(P-P/i));
for(i=1;i<=N;++i)a[i].x=read(),a[i].y=read(),a[i].id=i;
std::sort(a+1,a+N+1,cmp);
for(i=N;i;--i)b[i]=N-i-G(a[i].y),C(a[i].y,1);
memset(T,0,sizeof T);C(1,1);
for(i=1;i<=N;++i)
{
int V=G(a[i].y);
if(b[i])C(a[i].y,Mul(V,inv[b[i]]));
else ans[a[i].id]=V;
}
int fac=1;for(i=1;i<N;++i)fac=Mul(fac,i);
for(i=1;i<=N;++i)printf("%d
",Mul(fac,ans[i]));
return 0;
}
Blog来自PaperCloud,未经允许,请勿转载,TKS!