难得周六晚上有比赛打,于是就报了下,结果竟然锕氪了 极度舒适
T1 序列问题
先开的这题。
一眼看这个数据范围,应该是要推式子了,先分个类。
-
\(q >= n\) :不用说了,肯定没方案。
-
\(0 <= q < n\)
发现只用考虑 \(\mathrm{B}\) 集合的最小值 \(-\) \(\mathrm{A}\) 集合的最大值 即可,考虑枚举,先枚举 \(\mathrm{B}\) 再枚举 \(\mathrm{A}\) ,设总的方案数为 \(ans\) ,可得到:
\[\begin{aligned} ans &= \sum_{i = q + 1}^{n} 2^{n - i} \sum_{j = 1}^{i - q} 2^{j - 1} \\ &= \sum_{i = q + 1}^{n} 2^{n - i} \times (2^{i-q}-1) \\ &= \sum_{i = q + 1}^{n} 2^{n - q} - 2^{n - i} \\ &= 2^{n - q} \times (n - q) - (2 ^ {n - q} - 1) \end{aligned}\]然后就能做到 \(\log n\) 了。
-
\(-n < q < 0\)
还是考虑枚举,先令 \(q = -q\) 变为正数,现在考虑的限制就是 \(\mathrm{A}\) 集合的最大值 \(-\) \(\mathrm{B}\) 集合的最小值 \(<= q\) 。
可以只枚举 \(\mathrm{A}\) ,然后就能确定 \(\mathrm{B}\) 集合可以取的数的个数,则:
\[ans = \sum_{i = 1}^{n} 2^{i - 1} \times (2^{n - \max(0, i - q - 1)} - 1) \]有个取 \(\max\) 的很烦,考虑分类来算:
\[\begin{aligned} ans1 &= \sum_{i = 1}^{q + 1} 2^{i - 1} \times (2^n - 1) \\ &= (2^{q + 1} - 1) \times 2^n - \sum_{i = 1}^{q + 1} 2^i \\ ans2 &= \sum_{i = q + 2}^{n} 2^{i - 1} \times (2^{n - (i - q - 1)} - 1) \\ &= \sum_{i = q + 2}^{n} 2^{n + q} - 2^{i - 1} \\ &= 2^{n + q} \times (n - q - 1) - \sum_{i = q + 2}^{n} 2^i \end{aligned}\]整合 \(ans1\) 与 \(ans2\) 得:
\[\begin{aligned} ans &= (2^{q + 1} - 1) \times 2^n + 2^{n + q} \times (n - q - 1) - \sum_{i = 1}^n 2^{i - 1} \\ &= (2^{q + 1} - 1) \times 2^n + 2^{n + q} \times (n - q - 1) - (2^n - 1) \end{aligned}\]然后就能做到 \(\log n\) 了。
-
\(q <= -n\) : 很明显怎么选都可以,直接输出总方案 \((2^n - 1) ^ 2\) 即可。
\(code:\)
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long LL;
LL read() {
char ch = getchar();
LL x = 0, pd = 0;
while (ch < '0' || ch > '9')
pd ^= ch == '-', ch = getchar();
while ('0' <= ch && ch <= '9')
x = x*10+(ch^48), ch = getchar();
return pd ? -x : x;
}
const int mod = 998244353;
int Add(int x, int y) { return x + y < mod ? x + y : x + y - mod; }
int Mul(LL x, LL y) { x %= mod, y %= mod; return 1ll * x * y % mod; }
int fastpow(int x, LL y) {
int res = 1;
for (; y; x = Mul(x, x), y >>= 1)
if (y & 1) res = Mul(res, x);
return res;
}
int main() {
#ifdef LOCAL
freopen("data.in", "r", stdin);
freopen("pro.out", "w", stdout);
#endif
int T = read();
while (T--) {
LL n = read(), q = read();
if (q >= 0) {
if (q < n) printf("%d\n", Mul(fastpow(2, n - q), n - q - 1) + 1);
else puts("0");
}
else {
q = -q;
if (q < n - 1)
printf("%d\n", Add(Add(Mul(fastpow(2, q + 1) - 1, fastpow(2, n)), Mul(fastpow(2, n + q), n - q - 1)), mod - fastpow(2, n) + 1));
else printf("%d\n", Mul(fastpow(2, n) - 1, fastpow(2, n) - 1));
}
}
return 0;
}
T2 俄罗斯方块
最后10min才写出来 ...
想了dp啊啥的感觉都没有思路,看到数据范围只有 \(10\) 个方块,方块就 \(3\) 种,棋盘只有 \(7 \times 6\) ,而且行消除了就真的没了,相当于棋盘在缩小,试着暴搜看能不能过吧。
觉得写递归回溯太麻烦了,用了类似 \(\mathrm{BFS}\) 的方法,开了两个存所有可能的棋盘状态的 \(\mathrm{vector}\) ,交替更新。
第一种方块旋转共有两个不同的形状,第二种就一个,第三种有六个(写的时候还漏了像镰刀形状的情况.....)
然后就是枚举插入在什么地方,判断合法性,然后插完之后判断消除,总之就是码码码......
码了150多行终于过了。
\(code:\)
#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;
int read() {
char ch = getchar();
int x = 0, pd = 0;
while (ch < '0' || ch > '9')
pd ^= ch == '-', ch = getchar();
while ('0' <= ch && ch <= '9')
x = x*10+(ch^48), ch = getchar();
return pd ? -x : x;
}
int n, ans = 7;
struct R {
int n;
int s[7];
} t;
vector<R>v[2];
R check(R x) {
for (int i = x.n - 1; i >= 0; i--)
if (x.s[i] == 63) {
for (int j = i; j < x.n - 1; j++) x.s[j] = x.s[j + 1];
--x.n;
}
ans = min(ans, x.n);
return x;
}
int main() {
#ifdef LOCAL
freopen("data.in", "r", stdin);
freopen("pro.out", "w", stdout);
#endif
n = read();
t.n = 7;
v[1].push_back(t);
for (int i = 1; i <= n; i++) {
int x = read(), len = v[i & 1].size();
v[i & 1 ^ 1].clear();
if (x == 1) {
for (int j = 0; j < len; j++) {
for (int k = 0; k < v[i & 1][j].n; k++)
for (int l = 0; l < 3; l++) {
int now = 15 << l;
if (!k || (now & v[i & 1][j].s[k - 1])) {
bool ok = 1;
for (int kk = k; ok && kk < v[i & 1][j].n; kk++)
ok = !(v[i & 1][j].s[kk] & now);
if (!ok) continue;
t = v[i & 1][j];
t.s[k] |= now;
v[i & 1 ^ 1].push_back(check(t));
}
}
for (int k = 0; k + 3 < v[i & 1][j].n; k++)
for (int l = 0; l < 6; l++) {
if (k && !(1 << l & v[i & 1][j].s[k - 1])) continue;
t = v[i & 1][j];
bool ok = 1;
for (int kk = k; ok && kk < v[i & 1][j].n; kk++)
ok = !(1 << l & v[i & 1][j].s[kk]);
if (!ok) continue;
t.s[k] |= 1 << l, t.s[k + 1] |= 1 << l, t.s[k + 2] |= 1 << l, t.s[k + 3] |= 1 << l;
v[i & 1 ^ 1].push_back(check(t));
}
}
}
if (x == 2) {
for (int j = 0; j < len; j++) {
for (int k = 0; k + 1 < v[i & 1][j].n; k++)
for (int l = 0; l < 5; l++) {
int now = 3 << l;
if (!k || (now & v[i & 1][j].s[k - 1])) {
bool ok = 1;
for (int kk = k; ok && kk < v[i & 1][j].n; kk++)
ok = !(v[i & 1][j].s[kk] & now);
if (!ok) continue;
t = v[i & 1][j];
t.s[k] |= now, t.s[k + 1] |= now;
v[i & 1 ^ 1].push_back(check(t));
}
}
}
}
if (x == 3) {
for (int j = 0; j < len; j++) {
for (int k = 0; k + 2 < v[i & 1][j].n; k++)
for (int l = 0; l < 5; l++) {
int now = 3 << l;
if (!k || (now & v[i & 1][j].s[k - 1])) {
bool ok = 1;
for (int kk = k; ok && kk < v[i & 1][j].n; kk++)
ok = !(v[i & 1][j].s[kk] & now);
if (!ok) continue;
t = v[i & 1][j];
t.s[k] |= now, t.s[k + 1] |= 1 << l, t.s[k + 2] |= 1 << l;
v[i & 1 ^ 1].push_back(check(t));
t = v[i & 1][j];
t.s[k] |= now, t.s[k + 1] |= 1 << (l + 1), t.s[k + 2] |= 1 << (l + 1);
v[i & 1 ^ 1].push_back(check(t));
}
}
for (int k = 0; k + 1 < v[i & 1][j].n; k++)
for (int l = 0; l < 4; l++) {
int now = 7 << l;
if (!k || (now & v[i & 1][j].s[k - 1])) {
bool ok = 1;
for (int kk = k; ok && kk < v[i & 1][j].n; kk++)
ok = !(v[i & 1][j].s[kk] & now);
if (!ok) continue;
t = v[i & 1][j];
t.s[k] |= now, t.s[k + 1] |= 1 << l;
v[i & 1 ^ 1].push_back(check(t));
t = v[i & 1][j];
t.s[k] |= now, t.s[k + 1] |= 1 << (l + 2);
v[i & 1 ^ 1].push_back(check(t));
}
}
for (int k = 0; k + 2 < v[i & 1][j].n; k++)
for (int l = 0; l < 5; l++) {
int now = 3 << l;
if (!k || (1 << l & v[i & 1][j].s[k - 1])) {
bool ok = 1;
for (int kk = k; ok && kk < v[i & 1][j].n; kk++)
ok = !(1 << l & v[i & 1][j].s[kk]);
for (int kk = k + 2; ok && kk < v[i & 1][j].n; kk++)
ok = !(1 << (l + 1) & v[i & 1][j].s[kk]);
if (!ok) continue;
t = v[i & 1][j];
t.s[k + 2] |= now, t.s[k + 1] |= 1 << l, t.s[k] |= 1 << l;
v[i & 1 ^ 1].push_back(check(t));
}
if (!k || (1 << (l + 1) & v[i & 1][j].s[k - 1])) {
bool ok = 1;
for (int kk = k; ok && kk < v[i & 1][j].n; kk++)
ok = !(1 << (l + 1) & v[i & 1][j].s[kk]);
for (int kk = k + 2; ok && kk < v[i & 1][j].n; kk++)
ok = !(1 << l & v[i & 1][j].s[kk]);
if (!ok) continue;
t = v[i & 1][j];
t.s[k + 2] |= now, t.s[k + 1] |= 1 << (l + 1), t.s[k] |= 1 << (l + 1);
v[i & 1 ^ 1].push_back(check(t));
}
}
}
}
}
printf("%d\n", 7 - ans);
return 0;
}
赛后别人提交的代码,我靠,就两三个跟我一样正正经经的码正解,其他人都是面向数据编程(打表),毕竟可以无限次提交,然后还能看到分数,感觉这样做好没意义啊。(所以应该就只有两三个人ak了hahaha)
T3 旅行没有商问题
避开T2大毒瘤先做了这题,事实证明也是明智的。
观察到数据范围很小,马上想到容斥+矩乘,算了下时间能过。
就是强制不能经过哪些点,然后剩下的问题就是随便走,就是一个很简单的图上dp了,矩阵加速一下就好。
感觉这题是这场比赛最简单的一道
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long LL;
int read() {
char ch = getchar();
int x = 0, pd = 0;
while (ch < '0' || ch > '9')
pd ^= ch == '-', ch = getchar();
while ('0' <= ch && ch <= '9')
x = x*10+(ch^48), ch = getchar();
return pd ? -x : x;
}
const int mod = 1000000007;
int Add(int x, int y) { return x + y < mod ? x + y : x + y - mod; }
int Mul(int x, int y) { return 1ll * x * y % mod; }
int n, m, p, q, ans;
int nd[25];
int mp[25][25];
int a[25], b[25][25], t[25][25];
void Mul() {
for (int j = 1; j <= n; j++) t[1][j] = 0;
for (int j = 1; j <= n; j++)
for (int k = 1; k <= n; k++)
t[1][j] = Add(t[1][j], Mul(a[k], b[k][j]));
for (int j = 1; j <= n; j++) a[j] = t[1][j];
}
void MulSelf() {
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++) t[i][j] = 0;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
for (int k = 1; k <= n; k++)
t[i][j] = Add(t[i][j], Mul(b[i][k], b[k][j]));
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++) b[i][j] = t[i][j];
}
int main() {
#ifdef LOCAL
freopen("data.in", "r", stdin);
freopen("pro.out", "w", stdout);
#endif
n = read(), m = read(), p = read(), q = read();
for (int i = 0; i < q; i++) nd[i] = read();
for (int i = 1; i <= m; i++) {
int x = read(), y = read();
mp[x][y] = mp[y][x] = 1;
}
for (int i = 0; i < (1 << q); i++) {
for (int j = 1; j <= n; j++) {
a[j] = 1;
for (int k = 1; k <= n; k++) b[j][k] = mp[j][k];
}
int cnt = 0;
for (int j = 0; j < q; j++)
if (1 << j & i) {
a[nd[j]] = 0;
for (int k = 1; k <= n; k++) b[k][nd[j]] = b[nd[j]][k] = 0;
++cnt;
}
int N = p - 1;
while (N) {
if (N & 1) Mul();
MulSelf(), N >>= 1;
}
int sum = 0;
for (int i = 1; i <= n; i++) sum = Add(sum, a[i]);
if (cnt & 1) ans = Add(ans, mod - sum);
else ans = Add(ans, sum);
}
printf("%d\n", ans);
return 0;
}