Description
- 有(n)个人,第(i)个人一开始在((0,a_i)),最后要走到((i,0)),每次只能从((i,j))走到((i+1,j))或者((i,j-1))。
- 求使得路径两两不相交的方案数,对(998244353)取模。经过同一点算相交。
- (a_i≤a_{i+1},n≤5*10^5,0≤a_i<10^6)。
Solution
- 假设只有两个人,我们交换他们的终点,他们的路径必然相交;可以发现交换终点后的方案包含了所有相交方案。
- 当(n>2)时,发现这就是一个容斥的问题。设排列(sigma)的方案数为(omega(sigma)),其逆序对数为(N(sigma)),答案应该是(sum_{sigma} (-1)^{N(sigma)}omega(sigma))。
- 实际上有一个叫LGV引理的东西。这篇专栏里有一点介绍:https://zhuanlan.zhihu.com/p/64482425。
- 然后OI Wiki上的例题就是本题的弱化版……
- 根据LGV引理,我们要求(A_{i,j}=C_{a_i+j}^j)的行列式。每一列将(frac 1{j!})提出来,就变成((a_i+1)^overline j)。随便归纳可知,我们可以通过初等变换把它消成((a_i+1)^j)。
- 范德蒙德行列式:一个方阵(A)中的每一列都是以(x_i)为公比的等比数列,则(det A=prod_{i>j}(x_i-x_j))。这个也可以随便归纳证明。
- 本题最后的式子就变成了:
- 后面的那个东西要卷积。具体地说,我们把(sum x^{a_i})和(sum x^{-a_i})卷起来,结果中([x^k])的意义即是差为(k)的次数,然后用个快速幂乘起来。由于这是指数,正常来说应该对(998244352)取模;但如果起点相同我们可以直接判掉,这样(a_i)就两两不同,一个差的出现次数最多只有(n/2)。
- 时间复杂度(O(nlog n))。
Code
#include<cstdio>
#include<algorithm>
#define P(x,y) x=(x+y)%mo
#define T(x,y) x=x*(y)%mo
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define go(i,a,b) for(int i=a;i< b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
typedef long long ll;
const ll mo=998244353;
ll fpow(ll x,ll y) {ll a=1; for(;y;y/=2,T(x,x))if(y&1)T(a,x); return a;}
namespace NTT
{
const int NM=1<<22;
int tf[NM]; ll w[NM];
void build(int n)
{
for(int i=1;i<n;i*=2)
{
w[i]=1; ll v=fpow(3,(mo-1)/2/i);
go(j,1,i) w[i+j]=w[i+j-1]*v%mo;
}
}
void DFT(ll a[NM],int n,bool f)
{
go(i,1,n)
{
tf[i]=tf[i/2]/2+(i&1)*(n/2);
if(i<tf[i]) swap(a[i],a[tf[i]]);
}
ll v;
for(int i=1;i<n;i*=2) for(int j=0;j<n;j+=2*i) go(k,0,i)
v=a[i+j+k]*w[i+k]%mo, a[i+j+k]=(a[j+k]-v+mo)%mo, P(a[j+k],v);
if(f)
{
reverse(a+1,a+n);
v=fpow(n,mo-2);
go(i,0,n) T(a[i],v);
}
}
void mtp(ll a[NM],ll b[NM],int n1)
{
DFT(a,n1,0); DFT(b,n1,0);
go(i,0,n1) T(a[i],b[i]);
DFT(a,n1,1);
}
}
using namespace NTT;
const int N=1<<22,M=1e6;
int n;
ll x,la,fac[N]={1},a[N],b[N],ans=1;
int main()
{
freopen("path.in","r",stdin);
freopen("path.out","w",stdout);
scanf("%d",&n);
fo(i,1,n)
{
scanf("%lld",&x), a[x]++, b[M-x]++, T(ans,x+1), fac[i]=fac[i-1]*i%mo;
if(i>1&&x==la) {puts("0"); return 0;}
la=x;
}
x=fpow(fac[n],mo-2);
fd(i,n,1) T(ans,x), T(x,i);
build(N/2);
mtp(a,b,N/2);
fo(i,M+1,M*2) if(a[i]) T(ans,fpow((i-M+mo)%mo,a[i]));
printf("%lld",ans);
}