1. 程式人生 > >[容斥原理 DP] BZOJ 4767 兩雙手

[容斥原理 DP] BZOJ 4767 兩雙手

首先一個向量可以被兩個向量唯一表示 然後就轉化為有障礙點的網格圖路徑計數
這應該是個經典的容斥模型 果然是NOIP模擬賽啊
如果不考慮障礙 那麼答案是path(s,t)=Cmn+m
然後我們考慮容斥 列舉路徑上碰到的第一個障礙點v

Ans=Cmn+mfvpath(v,t)
其中fv表示從起點s出發不經過其他障礙點到達v的路徑數 path(v,t) 表示從vt不考慮障礙的路徑數 這顯然也是個組合數
fv怎麼求 從頭開始DP就好了
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<set>
#define read(x) scanf("%d",&(x)) using namespace std; typedef pair<int,int> abcd; typedef long long ll; const int P=1e9+7; const int MAXN=1000005; ll fac[MAXN],inv[MAXN]; inline void Pre(int n=500000){ fac[0]=1; for (int i=1;i<=n;i++) fac[i]=fac[i-1]*i%P; inv[1]=1; for (int i=2;i<=n;i++) inv[i]=(ll)(P-P/i)*inv
[P%i]%P; inv[0]=1; for (int i=1;i<=n;i++) inv[i]=inv[i]*inv[i-1]%P; } inline ll C(int n,int m){ return fac[n]*inv[m]%P*inv[n-m]%P; } int ex,ey,ax,ay,bx,by; inline bool calc(int x,int y,int &A,int &B){ int s=x*by-y*bx,t=ax*by-ay*bx; if (s%t) return 0; else A=s/t; s=x*ay-y*ax,t=bx*ay
-by*ax; if (s%t) return 0; else B=s/t; return 1; } const int N=505; #define X first #define Y second int n,m; int tot; abcd pt[N]; int f[N]; inline ll path(int j,int i){ int dx=pt[i].X-pt[j].X; int dy=pt[i].Y-pt[j].Y; if (dx<0 || dy<0) return 0; return C(dx+dy,dx); } int main(){ int A,B; int x,y; Pre(); freopen("t.in","r",stdin); freopen("t.out","w",stdout); read(ex); read(ey); read(tot); read(ax); read(ay); read(bx); read(by); if (!(calc(ex,ey,A,B) && A>=0 && B>=0)) return printf("0\n"),0; else n=A,m=B; int tmp=tot; tot=0; for (int i=1;i<=tmp;i++){ read(x); read(y); if (calc(x,y,A,B) && A>=0 && B>=0 && A<=n && B<=m) pt[++tot]=abcd(A,B); } pt[++tot]=abcd(n,m); sort(pt+1,pt+tot+1); f[0]=1; for (int i=1;i<=tot;i++){ f[i]=path(0,i); for (int j=1;j<i;j++) f[i]=(f[i]+P-(ll)f[j]*path(j,i)%P)%P; } printf("%d\n",f[tot]); return 0; }