题解 小 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;
}
posted @ 2022-03-24 20:11  Administrator-09  阅读(2)  评论(0编辑  收藏  举报