头一回凭自己本事把一整场AGC的题都补了,补完发现有些题做法和官方做法不太一样,所以还是有些记录价值的。
A - Connection and Disconnection
显然总的来说每个长为 L L L 的连续段恰改掉 ⌊ L 2 ⌋ \left\lfloor \frac L2 \right\rfloor ⌊2L⌋ 个字符,为了计算这一总和再看看是否首位相等,整段相等。
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <ctime>
#include <cctype>
#include <algorithm>
#include <random>
#include <bitset>
#include <queue>
#include <functional>
#include <set>
#include <map>
#include <vector>
#include <chrono>
#include <iostream>
#include <limits>
#include <numeric>
#define LOG(FMT...) fprintf(stderr, FMT)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
// mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());
const int N = 110;
char s[N];
int main() {
int n, k;
scanf("%s%d", s + 1, &k);
n = strlen(s + 1);
if (count(s + 1, s + n + 1, s[1]) == n)
printf("%lld\n", n * (ll)k / 2);
else {
ll ans = 0;
if (s[1] != s[n]) {
char c = s[1];
int cnt = 1;
for (int i = 2; i <= n + 1; ++i)
if (c == s[i])
++cnt;
else {
ans += cnt / 2LL * k;
cnt = 1;
c = s[i];
}
} else {
int l = 1, r = n;
while (s[l] == s[1]) ++l;
while (s[r] == s[n]) --r;
char c = s[l];
int cnt = 1;
for (int i = l + 1; i <= r + 1; ++i)
if (c == s[i])
++cnt;
else {
ans += cnt / 2LL * k;
cnt = 1;
c = s[i];
}
ans += (l - 1 + n - r) / 2LL * (k - 1);
ans += (l - 1) / 2;
ans += (n - r) / 2;
}
printf("%lld\n", ans);
}
return 0;
}
B - Graph Partition
显然必须是二分图,其次不难发现找到一个离所有点最长的最短路,最小化这一值就是答案,跑一遍 Floyd 即可。
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <ctime>
#include <cctype>
#include <algorithm>
#include <random>
#include <bitset>
#include <queue>
#include <functional>
#include <set>
#include <map>
#include <vector>
#include <chrono>
#include <iostream>
#include <limits>
#include <numeric>
#define LOG(FMT...) fprintf(stderr, FMT)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
// mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());
const int N = 210;
int n;
char s[N][N];
int d[N][N];
int vis[N];
void dfs(int u) {
for (int v = 1; v <= n; ++v)
if (s[u][v] == '1')
if (!vis[v]) {
vis[v] = -vis[u];
dfs(v);
} else if (vis[v] == vis[u]) {
puts("-1");
exit(0);
}
}
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; ++i)
scanf("%s", s[i] + 1);
vis[1] = 1;
dfs(1);
for (int i = 1; i <= n; ++i)
fill(d[i] + 1, d[i] + n + 1, n);
for (int i = 1; i <= n; ++i)
d[i][i] = 0;
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= n; ++j)
if (s[i][j] == '1')
d[i][j] = 1;
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= n; ++j)
for (int k = 1; k <= n; ++k)
d[j][k] = min(d[j][k], d[j][i] + d[i][k]);
int ans = 0;
for (int i = 1; i <= n; ++i)
ans = max(ans, *max_element(d[i] + 1, d[i] + n + 1));
printf("%d\n", ans + 1);
return 0;
}
C - Division by Two with Something
通过循环节容斥。
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <ctime>
#include <cctype>
#include <algorithm>
#include <random>
#include <bitset>
#include <queue>
#include <functional>
#include <set>
#include <map>
#include <vector>
#include <chrono>
#include <iostream>
#include <limits>
#include <numeric>
#define LOG(FMT...) fprintf(stderr, FMT)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
// mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());
const int N = 200010, P = 998244353;
int n;
int sieve[N * 2];
bool vs[N];
char s[N];
int mpow(int x, int k) {
int ret = 1;
while (k) {
if (k & 1)
ret = ret * (ll)x % P;
x = x * (ll)x % P;
k >>= 1;
}
return ret;
}
int norm(int x) { return x >= P ? x - P : x; }
void add(int& x, int y) {
if ((x += y) >= P)
x -= P;
}
void sub(int& x, int y) {
if ((x -= y) < 0)
x += P;
}
int gcd(int a, int b) { return b ? gcd(b, a % b) : a; }
int solve(int x) {
if (x / gcd(x, n) % 2)
return 0;
int p = 0;
bool cur = false;
int cnt = 0;
do {
vs[p] = cur;
cur ^= ((p + x) / n) & 1;
p = (p + x) % n;
++cnt;
} while (p != 0);
int sz = n / cnt, ret = 0;
for (int i = 1; i <= sz; ++i)
ret = (ret * 2 + s[n - i]) % P;
for (int i = n - 1; i; --i) {
int p = i % sz;
bool dig = s[n - sz + p] ^ vs[i - p] ^ vs[n - sz];
if (dig > s[i])
return ret;
if (dig < s[i])
return norm(ret + 1);
}
return norm(ret + 1);
}
int main() {
scanf("%d%s", &n, s);
reverse(s, s + n);
for (int i = 0; i < n; ++i)
s[i] -= '0';
for (int i = 1; i <= n * 2; ++i)
if (n * 2 % i == 0)
sieve[i] = i;
for (int i = n * 2; i; --i)
for (int j = i + i; j <= n * 2; j += i)
sub(sieve[i], sieve[j]);
int ans = 0;
for (int i = 1; i <= n * 2; ++i)
if (sieve[i]) {
ans = (ans + sieve[i] * (ll)solve(i)) % P;
}
printf("%d\n", ans);
return 0;
}
D - Incenters
数学题怎么回事
考虑本题的重要条件是所有点都分布在同一个圆上,不难通过倒弧和角得到一个好用的结论:中心就是一个角平分线打到对面的交点处为圆心,以这一圆心到另外两点(显然距离相等)为半径画圆,和原来那条角平分线的交点。进一步选一个点的时候这个东西可以把每个点做出的圆的一个向量集体做同一个线性变换,因此可以
Θ
(
n
2
)
\Theta(n^2)
Θ(n2) 计算。
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <ctime>
#include <cctype>
#include <algorithm>
#include <complex>
#include <random>
#include <bitset>
#include <queue>
#include <functional>
#include <set>
#include <map>
#include <vector>
#include <chrono>
#include <iostream>
#include <limits>
#include <numeric>
#define LOG(FMT...) fprintf(stderr, FMT)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
// mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());
template <class T>
istream& operator>>(istream& is, vector<T>& v) {
for (T& x : v)
is >> x;
return is;
}
template <class T>
ostream& operator<<(ostream& os, const vector<T>& v) {
if (!v.empty()) {
os << v.front();
for (int i = 1; i < v.size(); ++i)
os << ' ' << v[i];
}
return os;
}
typedef complex<double> C;
const double PI = acos(-1.);
int main() {
#ifdef LBT
freopen("test.in", "r", stdin);
int nol_cl = clock();
#endif
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, l;
cin >> n >> l;
vector<int> t(n);
cin >> t;
vector<double> theta(n);
transform(t.begin(), t.end(), theta.begin(), [&](int t) { return 2 * PI * t / l; });
function<C(double)> unit = [](double theta) {
return C(cos(theta), sin(theta));
};
C ans = 0;
C sum = 0;
for (int i = 1; i < n - 1; ++i) {
sum += unit(theta[i - 1] * .5);
for (int j = i + 1; j < n; ++j) {
C center = unit((theta[i] + theta[j]) * .5);
double r = sin((theta[j] - theta[i]) * .25) * 2;
C relative = sum * unit((theta[i] + theta[j]) * .25 - PI * .5);
ans += center * (double)i + r * relative;
}
}
ans /= n * (n - 1) * (n - 2LL) / 6;
printf("%.15f %.15f\n", ans.real(), ans.imag());
#ifdef LBT
LOG("Time: %dms\n", int ((clock()
-nol_cl) / (double)CLOCKS_PER_SEC * 1000));
#endif
return 0;
}
E - Pairing Points
随便 DP 搞搞,重点状态 f ( l , m , r ) f(l, m, r) f(l,m,r) 表示外部向 [ l , r ] [l, r] [l,r] 内连进来了一条边接到 m ( l ≤ m ≤ r ) m (l\le m\le r) m(l≤m≤r) 点,显见拆掉这条边之后就是两边若干个“哑铃”形状。优化一下计算顺序可以做到 Θ ( n 5 ) \Theta(n^5) Θ(n5),感觉官方的 Θ ( n 7 ) \Theta(n^7) Θ(n7) 太不走心了。
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <numeric>
#include <functional>
#include <vector>
#define LOG(FMT...) fprintf(stderr, FMT)
using namespace std;
typedef long long ll;
const int N = 41;
int n;
char a[N][N];
ll dp[N][N][N], dp2[N][N][N][N], dp3[N][N][N];
ll dfs2(int l1, int r1, int l2, int r2);
ll dfs(int l, int m, int r) {
if (dp[l][m][r] != -1)
return dp[l][m][r];
if (l == r)
return dp[l][m][r] = 1;
dp[l][m][r] = 0;
for (int i = l; i < m; i += 2)
for (int j = r; j > m; j -= 2)
dp[l][m][r] += dfs2(l, i, j, r) * dfs(i + 1, m, j - 1);
return dp[l][m][r];
}
ll dfs3(int p, int l, int r) {
if (dp3[p][l][r] != -1)
return dp3[p][l][r];
dp3[p][l][r] = 0;
for (int i = l; i <= r; ++i)
if (a[p][i])
dp3[p][l][r] += dfs(l, i, r);
return dp3[p][l][r];
}
ll dfs2(int l1, int r1, int l2, int r2) {
if (dp2[l1][r1][l2][r2] != -1)
return dp2[l1][r1][l2][r2];
dp2[l1][r1][l2][r2] = 0;
for (int i = l1; i <= r1; ++i)
dp2[l1][r1][l2][r2] += dfs(l1, i, r1) * dfs3(i, l2, r2);
return dp2[l1][r1][l2][r2];
}
int main() {
#ifdef LBT
freopen("test.in", "r", stdin);
#endif
scanf("%d", &n);
for (int i = 0; i < n * 2; ++i)
scanf("%s", a[i]);
for (int i = 0; i < n * 2; ++i)
for (int j = 0; j < n * 2; ++j)
a[i][j] -= '0';
memset(dp, -1, sizeof(dp));
memset(dp2, -1, sizeof(dp2));
memset(dp3, -1, sizeof(dp3));
printf("%lld\n", dfs3(0, 1, n * 2 - 1));
return 0;
}
F - Min Product Sum
考虑把问题看成从小往大往里面放数,每次放进去一个数当行当列没被确定最小值的数此时就被确定了。然后是一个类似于 CTS2019 D1T1 的容斥,直接看是 Θ ( K N 3 M 3 ) \Theta(KN^3M^3) Θ(KN3M3) 的,但是发现枚举多几行几列被选和几行几列容斥掉可以分步做,也就是当成负数个选择方法,这样就是 Θ ( K N 2 M 2 ) \Theta(KN^2M^2) Θ(KN2M2) 的。然后发现把一些 i a b i^{ab} iab 类似的部分分开剩下的是两个维度个卷上 e x e y \mathrm{e}^x\mathrm{e}^y exey,容斥过来是 e − x e − y \mathrm{e}^{-x}\mathrm{e}^{-y} e−xe−y,这部分做一维乘法暴力就是 Θ ( K N M ( N + M ) ) \Theta(KNM(N+M)) Θ(KNM(N+M))。
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <ctime>
#include <cctype>
#include <algorithm>
#include <random>
#include <bitset>
#include <queue>
#include <functional>
#include <set>
#include <map>
#include <vector>
#include <chrono>
#include <iostream>
#include <limits>
#include <numeric>
#define LOG(FMT...) fprintf(stderr, FMT)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef vector<int> vi;
// mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());
template<class T>
istream &operator>>(istream &is, vector<T> &v) {
for (T &x : v)
is >> x;
return is;
}
ostream &operator<<(ostream &os, const pair<char, int> &unit) {
return os << unit.first << "^" << unit.second;
}
template<class T>
ostream &operator<<(ostream &os, const vector<T> &v) {
if (!v.empty()) {
os << v.front();
for (int i = 1; i < v.size(); ++i)
os << ' ' << v[i];
}
return os;
}
const int N = 110;
int P;
int fac[N], ifac[N], nifac[N], pw1[N * N], pw2[N * N];
int mpow(int x, int k) {
int ret = 1;
while (k) {
if (k & 1)
ret = ret * (ll) x % P;
k >>= 1;
x = x * (ll) x % P;
}
return ret;
}
int norm(int x) { return x >= P ? x - P : x; }
void add(int &x, int y) {
if ((x += y) >= P)
x -= P;
}
void sub(int &x, int y) {
if ((x -= y) < 0)
x += P;
}
void exGcd(int a, int b, int &x, int &y) {
if (!b) {
x = 1;
y = 0;
return;
}
exGcd(b, a % b, y, x);
y -= a / b * x;
}
int inv(int a) {
int x, y;
exGcd(a, P, x, y);
return norm(x + P);
}
void prepare(int n) {
fac[0] = 1;
for (int i = 1; i <= n; ++i) fac[i] = fac[i - 1] * (ll) i % P;
ifac[1] = 1;
for (int i = 2; i <= n; ++i)
ifac[i] = -(P / i) * (ll) ifac[P % i] % P + P;
ifac[0] = 1;
for (int i = 1; i <= n; ++i)
ifac[i] = ifac[i - 1] * (ll) ifac[i] % P;
memcpy(nifac, ifac, sizeof(ifac));
for (int i = 1; i <= n; i += 2)
nifac[i] = P - nifac[i];
}
void gpw(int* pw, int x, int l) {
pw[0] = 1;
for (int k = 1; k <= l; ++k)
pw[k] = pw[k - 1] * (ll)x % P;
}
int f[N][N];
int main() {
#ifdef LBT
freopen("test.in", "r", stdin);
int nol_cl = clock();
#endif
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, m, k;
cin >> n >> m >> k >> P;
prepare(max(n, m));
f[0][0] = 1;
for (int i = 1; i <= k; ++i) {
gpw(pw1, inv(i), n * m);
gpw(pw2, inv(k - i + 1), n * m);
for (int a = 0; a <= n; ++a)
for (int b = 0; b <= m; ++b)
f[a][b] = f[a][b] * (ll) pw1[a * m + b * n - a * b] % P * fac[n - a] % P * fac[m - b] % P * pw2[a * b] % P;
for (int a = 0; a <= n; ++a)
for (int b = m; b; --b)
for (int j = 1; j <= b; ++j)
f[a][b] = (f[a][b] + f[a][b - j] * (ll)ifac[j]) % P;
for (int b = 0; b <= m; ++b)
for (int a = n; a; --a)
for (int j = 1; j <= a; ++j)
f[a][b] = (f[a][b] + f[a - j][b] * (ll)ifac[j]) % P;
gpw(pw2, k - i + 1, n * m);
for (int a = 0; a <= n; ++a)
for (int b = 0; b <= m; ++b)
f[a][b] = f[a][b] * (ll)pw2[a * b] % P;
if (i == k) {
int ans = f[n][m] * (ll)mpow(i, n * m) % P;
cout << ans;
break;
}
gpw(pw1, i, n * m);
gpw(pw2, inv(k - i), n * m);
for (int a = 0; a <= n; ++a)
for (int b = 0; b <= m; ++b)
f[a][b] = f[a][b] * (ll)pw2[a * b] % P;
for (int a = 0; a <= n; ++a)
for (int b = m; b; --b)
for (int j = 1; j <= b; ++j)
f[a][b] = (f[a][b] + f[a][b - j] * (ll)nifac[j]) % P;
for (int b = 0; b <= m; ++b)
for (int a = n; a; --a)
for (int j = 1; j <= a; ++j)
f[a][b] = (f[a][b] + f[a - j][b] * (ll)nifac[j]) % P;
gpw(pw2, k - i, n * m);
for (int c = 0; c <= n; ++c)
for (int d = 0; d <= m; ++d)
f[c][d] = f[c][d] * (ll) pw1[c * m + d * n - c * d] % P * ifac[n - c] % P * ifac[m - d] % P * pw2[c * d] % P;
}
#ifdef LBT
LOG("Time: %dms\n", int ((clock()
-nol_cl) / (double)CLOCKS_PER_SEC * 1000));
#endif
return 0;
}