NOI 十连测 Round 5 T2 运河计划
SOL:
不难发现,第二维坐标最小的红点一定对应第二维坐标最小的蓝 点,次小的、第三小的……均一一对应,否则线路必然会有交叉或者 根本无法修建。
下面我们讨论在给定一对红蓝点之间有多少条线路。 (1)不存在障碍点,显然条数为 C(Δx + Δy, Δx),Δx、Δy 分别为第 一、二维坐标差值; (2)存在障碍点,由于整张图是有向无环图,我们可以把红点、 蓝点及它们构成的矩形框中存在的紫点拓扑排序。记 F[i]表示从红点 到第 i 个关键点(可能是紫点或蓝点),中间不经过紫点的方案数, 这可以应用容斥 O(q2 )的解决。 对每对红蓝点间均应用上述过程,即得一个 O(pq2 )的算法解决不 考虑交叉的问题。
我们可以容斥上面的柿子,写出来震惊地发现是行列式。那么就过了。
#include<bits/stdc++.h> #define mo 998244353 #define LL long long #define N 200007 #define SIZ 2007 #define sight(c) ('0'<=c&&c<='9') inline void read(int &x){ static char c; for (c=getchar();!sight(c);c=getchar()); for (x=0;sight(c);c=getchar())x=x*10+c-48; } void write(int x){if (x<10) {putchar('0'+x); return;} write(x/10); putchar('0'+x%10);} inline void writeln(int x){ if (x<0) putchar('-'),x*=-1; write(x); putchar('\n'); } inline void writel(int x){ if (x<0) putchar('-'),x*=-1; write(x); putchar(' '); } using namespace std; struct Node{ int x,y,to; inline bool operator <(const Node &A)const{ return x==A.x?y<A.y:x<A.x; } }g[SIZ]; LL fac[N],ni[N],ans[SIZ],pp; inline LL c(int x,int y){ return (fac[x]*ni[y]%mo)*ni[x-y]%mo; } LL qsm(LL x,LL y){ static LL anw; for (anw=1;y;y>>=1,x=x*x%mo) if (y&1) anw=anw*x%mo; return anw; } void sol(LL* anw,int x){ memset(ans,0,sizeof ans); for (int i=1;i<=pp;i++) if (g[i].y>=x){ ans[i]=c(g[i].x+g[i].y-x,g[i].x); for (int j=1;j<i;j++) if ((g[j].to==0)&&g[j].y>=x&&g[j].x<=g[i].x&&g[j].y<=g[i].y) ans[i]=(ans[i]-ans[j]*c(g[i].x+g[i].y-g[j].x-g[j].y,g[i].y-g[j].y)%mo)%mo, ans[i]=(ans[i]+mo)%mo; if (g[i].to) anw[g[i].to]=ans[i]; } } void per(){ fac[0]=1; for (int i=1;i<N;i++) fac[i]=fac[i-1]*i%mo; ni[N-1]=qsm(fac[N-1],mo-2); for (int i=N-1;i;i--) ni[i-1]=ni[i]*i%mo; } LL ANS=1,t;int p,q,n,m,a[SIZ],b[SIZ]; LL f[SIZ][SIZ]; void doniv(){ for (int i=1;i<=p;i++) for (int j=i+1;j<=p;j++) while (f[j][i]){ t=f[i][i]/f[j][i]; for (int z=i;z<=p;z++){ f[i][z]=(f[i][z]-f[j][z]*t)%mo; if (f[i][z]<0) f[i][z]+=mo; swap(f[i][z],f[j][z]); } ANS*=-1; } for (int i=1;i<=p;i++) ANS=ANS*f[i][i]%mo; ANS=(ANS+mo)%mo; } signed main () { freopen("canal.in","r",stdin); freopen("canal.out","w",stdout); read(n); read(m); read(p); read(q); per(); for (int i=1;i<=p;i++) read(a[i]); for (int i=1;i<=p;i++) read(b[i]); sort(a+1,a+p+1); sort(b+1,b+p+1); for (int i=1;i<=q;i++) read(g[i].x),read(g[i].y); for (int i=1;i<=p;i++) g[i+q].x=n,g[i+q].y=b[i],g[i+q].to=i; sort(g+1,g+q+p+1); pp=q+p; for (int i=1;i<=p;i++) sol(f[i],a[i]); // for (int i=1;i<=p;i++,putchar('\n')) // for (int j=1;j<=p;j++) writel(f[i][j]); doniv(); // for (int i=1;i<=p;i++,putchar('\n')) // for (int j=1;j<=p;j++) writel(f[i][j]); writeln(ANS); return 0; }