题解 小 W 与骑士
赛时大概是脑子坏了
直接 DP 的话需要分层图而且值域大的离谱
首先发现给定两种操作相当于两个基向量
那么除去这两个向量共线的情况,所有点都可以由这两个向量唯一表示成 \(x\vec{a}+y\vec{b}\)
那么可以转换坐标系
将上面的点写成 \((x, y)\),那么现在变成了一个网格图中只能向上/右走,有 \(k\) 个禁止位置的问题
那么令 \(dp_i\) 为经过的唯一禁止位置为第 \(i\) 个位置的方案数
转移考虑所有方案减去不合法的,不合法的考虑枚举这条不合法路径上编号最小的禁止位置
那么这部分就做完了
回来看两向量共线
发现是一个一维问题,直接建图跑拓扑排序
最后若终点没有入栈则有无限解
点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 1000010
#define fir first
#define sec second
#define ll long long
#define int128 __int128
// #define int long long
char buf[1<<21], *p1=buf, *p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
inline int read() {
int ans=0, f=1; char c=getchar();
while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
return ans*f;
}
int x, y, k;
ll fac[N], inv[N];
const ll mod=1e9+7;
int ax, ay, bx, by, top;
pair<int, int> sta[N], point[N], ord;
inline ll C(int n, int k) {return n<k?0:fac[n]*inv[k]%mod*inv[n-k]%mod;}
namespace task1{
int now;
int dp[510];
pair<int, int> rotate(pair<int, int> pos) {
int x=pos.fir, y=pos.sec;
ll up=(x-y)*(ax+ay)-(x+y)*(ax-ay);
ll down=(bx-by)*(ax+ay)-(bx+by)*(ax-ay);
if (up%down) return {-1, -1};
ll t=up/down;
if (t<0) return {-1, -1};
if (ax+ay) up=(x+y)-t*(bx+by), down=ax+ay;
else up=(x-y)-t*(bx-by), down=ax-ay;
if (up%down) return {-1, -1};
ll s=up/down;
if (s<0) return {-1, -1};
return {s, t};
}
void solve() {
ord=rotate({x, y}); top=0;
if (ord.fir<0||ord.sec<0) {puts("0"); return ;}
memset(dp, 0, sizeof(dp));
for (int i=1; i<=k; ++i) {
pair<int, int> t=rotate(point[i]);
if (t.fir>=0&&t.fir<=ord.fir&&t.sec>=0&&t.sec<=ord.sec) sta[++top]=t;
}
sta[++top]=ord;
sort(sta+1, sta+top+1);
// cout<<"sta: "; for (int i=1; i<=top; ++i) cout<<"("<<sta[i].fir<<','<<sta[i].sec<<") "; cout<<endl;
dp[0]=1;
for (int i=1; i<=top; ++i) {
dp[i]=C(sta[i].fir+sta[i].sec, sta[i].fir);
for (int j=1; j<i; ++j) if (sta[j].fir<=sta[i].fir&&sta[j].sec<=sta[i].sec)
dp[i]=(dp[i]-dp[j]*C(sta[i].fir-sta[j].fir+sta[i].sec-sta[j].sec, sta[i].fir-sta[j].fir))%mod;
}
// cout<<"dp: "; for (int i=1; i<=top; ++i) cout<<dp[i]<<' '; cout<<endl;
printf("%lld\n", (dp[top]%mod+mod)%mod);
}
}
namespace task2{
queue<int> q;
map<pair<int, int>, int> mp;
map<pair<int, int>, bool> ban;
int head[5010], cnt[5010], dp[5010], ecnt, tot;
struct edge{int to, next;}e[N];
inline void add(int s, int t) {e[++ecnt]={t, head[s]}; head[s]=ecnt;}
inline int gcd(int a, int b) {return !b?a:gcd(b, a%b);}
void dfs(pair<int, int> u) {
if (mp.find(u)!=mp.end() || ban.find(u)!=ban.end() || max(llabs(u.fir), llabs(u.sec))>1000) return ;
mp[u]=++tot;
pair<int, int> v;
v={u.fir+ax, u.sec+ay}; dfs(v);
if (ban.find(v)==ban.end() && max(llabs(v.fir), llabs(v.sec))<=1000) add(mp[u], mp[v]);
if (ax==bx&&ay==by) return ;
v={u.fir+bx, u.sec+by}; dfs(v);
if (ban.find(v)==ban.end() && max(llabs(v.fir), llabs(v.sec))<=1000) add(mp[u], mp[v]);
}
void solve() {
if (ax*y!=ay*x) {puts("0"); return ;}
mp.clear(); ban.clear(); ecnt=tot=0;
memset(head, -1, sizeof(head));
memset(dp, 0, sizeof(dp));
memset(cnt, 0, sizeof(cnt));
for (int i=1; i<=k; ++i) ban[point[i]]=1;
dfs({0, 0});
if (mp.find({x, y})==mp.end()) {puts("0"); return ;}
for (int i=1; i<=ecnt; ++i) ++cnt[e[i].to];
for (int i=1; i<=tot; ++i) if (!cnt[i]) q.push(i), dp[i]=1;
while (q.size()) {
int u=q.front(); q.pop();
for (int i=head[u],v; ~i; i=e[i].next) {
v = e[i].to;
dp[v]=(dp[v]+dp[u])%mod;
if (--cnt[v]==0) q.push(v);
}
}
if (cnt[mp[{x, y}]]) puts("-1");
else printf("%d\n", dp[mp[{x, y}]]);
}
}
signed main()
{
freopen("knight.in", "r", stdin);
freopen("knight.out", "w", stdout);
int T=read();
fac[0]=fac[1]=1; inv[0]=inv[1]=1;
for (int i=2; i<N; ++i) fac[i]=fac[i-1]*i%mod;
for (int i=2; i<N; ++i) inv[i]=(mod-mod/i)*inv[mod%i]%mod;
for (int i=2; i<N; ++i) inv[i]=inv[i-1]*inv[i]%mod;
while (T--) {
x=read(); y=read(); k=read();
ax=read(); ay=read(); bx=read(); by=read();
for (int i=1; i<=k; ++i) point[i].fir=read(), point[i].sec=read();
if (ax*by==ay*bx) task2::solve();
else task1::solve();
}
return 0;
}