ABC168+172 E、F

ABC168
ABC172

ABC168 E ∙ (Bullet)

模拟赛时看错式子降智了。。。我是sb。
很显然吧,想到把带有 \(i\) 的式子独立出来。
\(\frac {A_i} {B_i}=-\frac {B_j} {A_j}\)。由于这个东西是有交换律的,即 \(\frac {A_j} {B_j}=-\frac {B_i} {A_i}\),不难想到将所有这样的 \(j\) 都和 \(i\) 一起放入一组,令值为 \(\frac {A_i} {B_i}\)\(len_i\) 个,则这一组的贡献为 \(2^{len_i}+2^{len_j}-1\),(这个减 \(1\) 为减掉重复的都不选的情况)。然后所有组可同时存在,乘起来即可。注意这里不能直接除,(可能会爆精度+\(/0\)),用 \(pair + map\) 即可,用 \(pair\) 前要除以个 \(gcd\)(为了统一)。注意特判 \(A_i=0,B_i=0\) 的情况。

#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <map>
#define int long long
using namespace std;
const int MAXN = 2e5 + 5, Mod = 1e9 + 7;
struct Node {
	int X, Y;
	bool operator < (const Node P) const { return X < P.X; } 
}arr[MAXN];
// fkyst.
int Gcd(int x, int y) {
	if(y == 0) return x;
	return Gcd(y, x % y);
}
// a[i]/b[i]==-b[j]/a[i]
int n, ans = 1, t1, t2, g[MAXN], c;
map <pair <int, int>, int> mp;
signed main() {
	scanf("%lld", &n); g[0] = 1;
	for(int i = 1; i <= n; i ++) g[i] = g[i - 1] * 2 % Mod; 
	for(int i = 1; i <= n; i ++) scanf("%lld%lld", &arr[i].X, &arr[i].Y);
	for(int i = 1; i <= n; i ++) {
		if(arr[i].X == 0 && arr[i].Y == 0) { c ++; continue; }
		int t = Gcd(arr[i].X, arr[i].Y);
		arr[i].X /= t; arr[i].Y /= t;
		if(arr[i].X < 0) arr[i].X = -arr[i].X, arr[i].Y = -arr[i].Y; 
		mp[make_pair(arr[i].X, arr[i].Y)] ++;
	}
	for(int i = 1; i <= n; i ++) {
		t1 = mp[make_pair(arr[i].X, arr[i].Y)]; mp[make_pair(arr[i].X, arr[i].Y)] = 0;
		if (-arr[i].Y < 0) arr[i].X = -arr[i].X, arr[i].Y = -arr[i].Y;
		t2 = mp[make_pair(-arr[i].Y, arr[i].X)], mp[make_pair(-arr[i].Y, arr[i].X)] = 0;
		ans = ans * (g[t1] + g[t2] - 1) % Mod;
//		printf("%d %d ")
	}
	printf("%lld", ((ans - 1 + c) % Mod + Mod) % Mod);
	return 0;
}
/*
2
0 2
2 0
*/

ABC168 F. (Single Dot)

模拟题!!!!!!!!!!
思路很简单,将横纵坐标分别离散化,暴力跑 \(bfs\)
难点:
1.一个“点”和一个 \(1\times1\) 的 “块” 坐标的转换。
2.题目给的坐标与计算机里的坐标的转换。
其实,这两点并不是这道题的难点,而是这类题的难点,在做之前不妨清晰地 理好关系,这样事半功倍。
以下为我的 code,感觉还是挺清晰的。

#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <queue>
#define LL long long
using namespace std;
const int MAXN = 3005;
int tox[5] = {1, -1, 0, 0}, toy[5] = {0, 0, 1, -1};
struct Node {
	int X, Y, Z;
	Node() {}
	Node(int x, int y) { X = x; Y = y; }
}Line1[MAXN], Line2[MAXN], t, q;
queue <Node> que;
int n, m, totx, toty, lshx[MAXN], lshy[MAXN], S, maxx;
long long ans;
bool v[MAXN][MAXN][4];
bool vis[MAXN][MAXN];
void Sadx(int &x) { x = lower_bound(lshx + 1, lshx + 1 + totx, x) - lshx; }
void Sady(int &x) { x = lower_bound(lshy + 1, lshy + 1 + toty, x) - lshy; }
long long Calc(int x, int y) { return (long long)(lshx[x + 1] - lshx[x]) * (lshy[y + 1] - lshy[y]); }
bool dfs(int x, int y) {
	Sadx(x); Sady(y);
	que.push(Node(x, y)); que.push(Node(x - 1, y)); que.push(Node(x, y - 1)); que.push(Node(x - 1, y - 1));
	vis[x][y] = 1; vis[x - 1][y] = 1; vis[x][y - 1] = 1; vis[x - 1][y - 1] = 1;
	ans += Calc(x, y); ans += Calc(x - 1, y); ans += Calc(x, y - 1); ans += Calc(x - 1, y - 1);
	while(!que.empty()) {
		t = que.front(); que.pop();
		for(int i = 0; i < 4; i ++) {
			q.X = t.X + tox[i]; q.Y = t.Y + toy[i];
			if(vis[q.X][q.Y] || v[t.X][t.Y][i]) continue;
//			printf("fight:%d %d -> %d %d\n", t.X, t.Y, q.X, q.Y);
			if(q.X < 1 || q.Y < 1 || q.X >= totx || q.Y >= toty) return 0;
			vis[q.X][q.Y] = 1; ans += Calc(q.X, q.Y); que.push(q);
		}
	}
	return 1;
}
int main() {
//	freopen("fuckit.out", "w", stdout);
	scanf("%d%d", &n, &m);
	for(int i = 1; i <= n; i ++) scanf("%d%d%d", &Line1[i].X, &Line1[i].Y, &Line1[i].Z), lshx[++ totx] = Line1[i].X, lshx[++ totx] = Line1[i].Y, lshy[++ toty] = Line1[i].Z;
	for(int i = 1; i <= m; i ++) scanf("%d%d%d", &Line2[i].X, &Line2[i].Y, &Line2[i].Z), lshx[++ totx] = Line2[i].X, lshy[++ toty] = Line2[i].Y, lshy[++ toty] = Line2[i].Z;
	lshx[++ totx] = 0; sort(lshx + 1, lshx + 1 + totx); totx = unique(lshx + 1, lshx + 1 + totx) - lshx - 1;
	lshy[++ toty] = 0; sort(lshy + 1, lshy + 1 + toty); toty = unique(lshy + 1, lshy + 1 + toty) - lshy - 1;
	for(int i = 1; i <= n; i ++) {
		Sadx(Line1[i].X); Sadx(Line1[i].Y); Sady(Line1[i].Z); 
//		printf("|%d %d %d %d\n", Line1[i].X, Line1[i].Z, Line1[i].Y, Line1[i].Z);
		for(int j = Line1[i].X; j < Line1[i].Y; j ++) v[j][Line1[i].Z - 1][2] = v[j][Line1[i].Z][3] = 1;
	}
	for(int i = 1; i <= m; i ++) {
		Sadx(Line2[i].X); Sady(Line2[i].Y); Sady(Line2[i].Z); 
		for(int j = Line2[i].Y; j < Line2[i].Z; j ++) v[Line2[i].X][j][1] = v[Line2[i].X - 1][j][0] = 1;
//		printf("|%d %d %d %d\n", Line2[i].X, Line2[i].Y, Line2[i].X, Line2[i].Z);
	}
//	for(int i = 1; i <= totx; i ++) {
//		for(int j = 1; j <= toty; j ++) {
//			printf("*");
//			if(v[i][j][2]) printf("|");
//			else printf(" ");
//		}
//		printf("\n");
//		for(int j = 1; j <= tot; j ++) {
//			if(v[i][j][0]) printf("__");
//			else printf("  ");
//		}
//		printf("\n");
//	}
	if(dfs(S, S)) printf("%lld", ans);
	else printf("INF");
	return 0;
}

ABC172 E NEQ

错排板子没看出来我是sb。
很容易想到将 \(a\) 序列 “固定”,求 \(b\) 序列的方案数和。你会发现现在有 \(n\) 个位置要填数,每个位置不能填自己,共有 \(m\) 个可填的数,这就是一个经典的错排,这里不展开,\(dp[i]=(n-1)\times (dp[i-1]+dp[i-2]+(m-n)*dp[i-1])\)

其实还可以用排列组合(容斥),枚举 \(i\) 个必须重复的元素,这里容斥很显然,不展开了。(

#include <cstdio>
#include <algorithm>
#include <cmath>
#define LL long long
using namespace std;
const int MAXN = 5e5 + 5, Mod = 1e9 + 7;
int n, m;
LL ans = 0, res, dp[MAXN], jc[MAXN], inv[MAXN];
LL Quick_Pow(LL x, int y) {
	LL ans = 1;
	for(; y; y >>= 1) {
		if(y & 1) ans = ans * x % Mod;
		x = x * x % Mod;
	}
	return ans;
}
LL C(int x, int y) { return jc[y] * inv[x] % Mod * inv[y - x] % Mod; }
LL A(int x, int y) { return jc[y] * inv[y - x] % Mod; }
int main() {
	scanf("%d%d", &n, &m); jc[0] = 1;
	for(int i = 1; i <= m; i ++) jc[i] = jc[i - 1] * i % Mod; inv[m] = Quick_Pow(jc[m], Mod - 2);
	for(int i = m - 1; i >= 0; i --) inv[i] = inv[i + 1] * (i + 1) % Mod;
	dp[0] = 1; 
	for(int i = 1; i <= n; i ++) {
		if(i >= 2) dp[i] = (i - 1) * dp[i - 2] % Mod;
		if(i >= 1) dp[i] = (dp[i] + (i - 1 + m - n) * dp[i - 1] % Mod) % Mod;
	}
	printf("%lld", A(n, m) * dp[n] % Mod);
	return 0;
}

ABC172 F Unfair Nim

一道 \(Nim\) 博弈的变形。即求最小的正整数 \(x\),使 \((a[1]-x)\oplus (a[2]+x)\oplus a[3]\oplus ...=0\)
即解方程:\(x+y=A(a[1]+a[2])\)\(x\oplus y=B(a[3]\oplus...)\)。发现加法不好按位做,一个很有用的性质:\(x+y=x\oplus y+2x\& y\)
\(x \& y=(A-B)/2\)。则直接贪心凑即可。正解代码就不贴了,考试时用搜索ri过去的。

posted @ 2021-07-14 21:08  Saintex  阅读(106)  评论(0编辑  收藏  举报