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;
}

 

posted @ 2018-03-27 21:34  泪寒之雪  阅读(377)  评论(0编辑  收藏  举报