ABC168+172 E、F
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过去的。