AtCoder Grand Contest 036 简要题解
从这里开始
Problem A Triangle
考虑把三角形移到和坐标轴相交,即
然后能够用坐标比较简单地计算面积,简单构造一下就行了。
Code
#include <bits/stdc++.h>
using namespace std;
typedef bool bolean; #define ll long long const int V = 1e9; ll S; int main() {
scanf("%lld", &S);
if (S == 1000000000000000000ll) {
printf("%d 0 0 0 0 %d\n", V, V);
return 0;
}
int z = S / V;
int y = V;
int x = 1;
int h = S % V;
printf("0 %d %d 0 %d %d\n", y, x, x + z, h);
return 0;
}
Problem B Do Not Duplicate
你发现 $x$ 如果还会再出现,会把之间的数都删掉,所以 $x$ 向它的后继连一条边。然后模拟一下就行了。
Code
#include <bits/stdc++.h>
using namespace std;
typedef bool boolean; #define ll long long const int N = 2e5 + 5; int n;
ll K;
int a[N], h[N];
int fa[N], c[N];
boolean vis[N]; int main() {
scanf("%d%lld", &n, &K);
for (int i = 0; i < n; i++) {
scanf("%d", a + i);
}
for (int i = n - 1; ~i; i--)
h[a[i]] = i + n;
for (int i = n - 1; ~i; i--) {
int suf = h[a[i]];
fa[i] = (suf + 1) % n;
c[i] = (suf + 1) / n;
h[a[i]] = i;
}
int p = 0, q;
vector<int> stk;
do {
vis[p] = true;
stk.push_back(p);
p = fa[p];
} while (!vis[p]);
int sumcost = 0;
do {
q = stk.back();
stk.pop_back();
sumcost += c[q];
} while (q ^ p);
stk.clear();
int x = 0;
while (K - c[x] >= 1 && (x ^ p))
K -= c[x], x = fa[x];
if (x == p)
K = (K - 1) % sumcost + 1;
while (K - c[x] >= 1)
K -= c[x], x = fa[x];
memset(vis, false, sizeof(vis));
for (int i = x; i < n + (K - 1) * n; i++) {
int z = a[i % n];
if (!vis[z]) {
stk.push_back(z);
vis[z] = true;
} else {
int y = 0;
do {
y = stk.back();
stk.pop_back();
vis[y] = false;
} while (y ^ z);
}
}
for (auto x : stk) {
printf("%d ", x);
}
return 0;
}
Problem C GP 2
考虑一些必要条件:
- 和为 $3M$
- 最大值不超过 $2M$
- 奇数个数不超过 $M$
可以对最大值和最大奇数或剩下的最大值进行讨论,然后用归纳法证明。
剩下是基础计数。
Code
#include <bits/stdc++.h>
using namespace std;
typedef bool boolean; #define ll long long void exgcd(int a, int b, int& x, int& y) {
if (!b) {
x = 1, y = 0;
} else {
exgcd(b, a % b, y, x);
y -= (a / b) * x;
}
} int inv(int a, int n) {
int x, y;
exgcd(a, n, x, y);
return (x < 0) ? (x + n) : (x);
} const int Mod = 998244353; template <const int Mod = :: Mod>
class Z {
public:
int v; Z() : v(0) { }
Z(int x) : v(x){ }
Z(ll x) : v(x % Mod) { } friend Z operator + (const Z& a, const Z& b) {
int x;
return Z(((x = a.v + b.v) >= Mod) ? (x - Mod) : (x));
}
friend Z operator - (const Z& a, const Z& b) {
int x;
return Z(((x = a.v - b.v) < 0) ? (x + Mod) : (x));
}
friend Z operator * (const Z& a, const Z& b) {
return Z(a.v * 1ll * b.v);
}
friend Z operator ~(const Z& a) {
return inv(a.v, Mod);
}
friend Z operator - (const Z& a) {
return Z(0) - a;
}
Z& operator += (Z b) {
return *this = *this + b;
}
Z& operator -= (Z b) {
return *this = *this - b;
}
Z& operator *= (Z b) {
return *this = *this * b;
}
friend boolean operator == (const Z& a, const Z& b) {
return a.v == b.v;
}
}; Z<> qpow(Z<> a, int p) {
Z<> rt = Z<>(1), pa = a;
for ( ; p; p >>= 1, pa = pa * pa) {
if (p & 1) {
rt = rt * pa;
}
}
return rt;
} typedef Z<> Zi; const int N = 3e6 + 5; int n, m;
Zi fac[N], _fac[N]; void prepare(int n) {
fac[0] = 1;
for (int i = 1; i <= n; i++)
fac[i] = fac[i - 1] * i;
_fac[n] = ~fac[n];
for (int i = n; i; i--)
_fac[i - 1] = _fac[i] * i;
}
Zi comb(int n, int m) {
return (n < m) ? (0) : (fac[n] * _fac[m] * _fac[n - m]);
} int main() {
scanf("%d%d", &n, &m);
prepare(3 * m + n - 1);
Zi ans = comb(3 * m + n - 1, n - 1) - comb(m + n - 2, n - 1) * n;
for (int num = m + 1; num <= n && num <= 3 * m; num++) {
if ((3 * m - num) & 1)
continue;
int sum = (3 * m - num) >> 1;
Zi tmp = comb(sum + n - 1, n - 1);
// mx is odd
tmp -= num * comb(sum - m + n - 1, n - 1);
// mx is even
tmp -= (n - num) * comb(sum - m + n - 2, n - 1);
tmp *= comb(n, num);
ans -= tmp;
}
printf("%d\n", ans.v);
return 0;
}
Problem D Negative Cycle
设到第 $i$ 个点的最短路为 $d_i$。不难发现 $d_i \geqslant d_{i + 1}$,如果存在负环,那么会更新。
考虑最短路等于 $d$ 和最短路等于 $d + 1$ 的两段 $[a, b]$,以及 $(b, c]$。
那么 $(b, c]$ 不能有到 $a$ 之前的边,否则 $d$ 会被更新。$(b + 1, c]$ 之间不能有从前连向后面的边,否则 $d$ 也会被更新。
状态中记入 $a, b$,稍作优化就能做到 $O(n^3)$。
Code
#include <bits/stdc++.h>
using namespace std;
typedef bool boolean; #define ll long long const ll llf = (signed ll) (~0ull >> 2); const int N = 505; int n;
int A[N][N];
ll s[N][N];
ll dp[N][N]; int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
if (i == j) {
continue;
}
scanf("%d", A[i] + j);
s[i][j] = A[i][j] + s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1];
}
}
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
dp[i][j] = llf;
dp[1][1] = 0;
for (int i = 2; i <= n; i++) {
dp[1][i] = dp[1][i - 1];
for (int j = 1; j < i; j++) {
dp[1][i] += A[j][i];
}
}
for (int i = 2; i <= n; i++) {
ll cost = 0;
for (int b = i - 1; b; b--) {
for (int j = b + 2; j <= i; j++)
cost += A[b + 1][j];
for (int a = b; a; a--) {
dp[b + 1][i] = min(dp[b + 1][i], dp[a][b] + cost + s[i][a - 1] - s[b][a - 1]);
}
}
}
ll ans = llf;
for (int i = 1; i <= n; i++)
ans = min(ans, dp[i][n]);
printf("%lld\n", ans);
return 0;
}
Problem E ABC String
先将连续的字符缩起来,设出现的次数满足 $a \leqslant b \leqslant c$。
现在我们希望删掉一些 $a$ 使得存在一种方案使得删掉一些 $b, c$ 使得 $a = b = c$。
不难发现,删掉一个 A 后至多使 $b$ 或 $c$ 减少 1。因此 A 仍然是出现次数最少的字符。
假设删除一些 A 之后,仍然满足 $b \leqslant c$。
如果 $b = c$,那么每次删掉 BC 或者 CB 就行了。
考虑 $b - c$ 所能到达的最小值,如果一段中含 B,那么可以贡献 -1,如果只有一个 C,那么会贡献 1。
因此,我们希望删掉一些 A,使得 $b - c$ 的最小值能小于等于 0。
不难发现,只用删掉若干个单独的 C 之前的 A,并且删掉一个 A 至多使单独的 C 减少 1 个。
Code
#include<bits/stdc++.h>
using namespace std;
typedef bool boolean; const int N = 1e6 + 5; int n;
char s[N], s1[N]; void shrink() {
int t = 0;
for (int i = 1; i <= n; i++) {
if (s[i] ^ s[i - 1]) {
s[++t] = s[i];
}
}
n = t;
} int cnt[3];
char pg[3], qg[3];
void trans() {
for (int i = 1; i <= n; i++) {
cnt[s[i] - 'A']++;
}
vector<pair<int, int>> a;
a.emplace_back(cnt[0], 0);
a.emplace_back(cnt[1], 1);
a.emplace_back(cnt[2], 2);
sort(a.begin(), a.end());
#define make_trans(x, y) pg[x] = y, qg[y] = x;
for (int i = 0; i < 3; i++)
make_trans(a[i].second, i);
for (int i = 1; i <= n; i++) {
s[i] = pg[s[i] - 'A'] + 'A';
}
} void make_valid() {
int x = 0, y = 0;
auto count = [&] (int l, int r) -> boolean {
if (l > r) return false;
for (int i = l; i <= r; i++) {
if (s[i] == 'B') {
x++;
return false;
}
}
if (l == 1 || r == n)
return false;
y++;
return true;
};
int ls = 0;
vector<int> pos;
for (int i = 1; i <= n; i++) {
if (s[i] == 'A') {
if (count(ls + 1, i - 1) && ls) {
pos.push_back(i);
}
ls = i;
}
}
count(ls + 1, n);
if (x < y) {
int t = 0;
pos.resize(y - x);
reverse(pos.begin(), pos.end());
for (int i = 1; i <= n; i++) {
if (!pos.empty() && i == pos.back()) {
pos.pop_back();
} else {
s[++t] = s[i];
}
}
n = t;
shrink();
s[n + 1] = 0;
}
memset(cnt, 0, sizeof(cnt));
for (int i = 1; i <= n; i++)
cnt[s[i] - 'A']++;
int d = cnt[2] - cnt[1];
int t = 0;
char target = 'C';
(d < 0) && (d = -d, target = 'B');
ls = 0;
auto remove = [&] (int l, int r) {
if (l > r)
return;
if ((l == 1 || r == n) || (l ^ r)) {
if (s[l] == target && d) l++, d--;
if (l < r && s[r] == target && d) r--, d--;
}
for (int i = l; i <= r; i++)
s1[++t] = s[i];
};
for (int i = 1; i <= n; i++) {
if (s[i] == 'A') {
remove(ls + 1, i - 1);
s1[++t] = 'A';
ls = i;
}
}
remove(ls + 1, n);
n = t;
for (int i = 1; i <= n; i++) {
s[i] = s1[i];
}
s[n + 1] = 0;
} void make_equal() {
memset(cnt, 0, sizeof(cnt));
for (int i = 1; i <= n; i++)
cnt[s[i] - 'A']++;
assert(cnt[1] == cnt[2]);
int d = cnt[1] - cnt[0];
int t = 0;
vector<pair<int, int>> pos;
for (int i = 1; i <= n; i++) {
if (i < n && d && s[i] != 'A' && s[i + 1] != 'A') {
if (s1[t] == 'A' && s[i + 2] == 'A') {
pos.emplace_back(t + 1, t + 2);
s1[++t] = s[i];
} else {
i++, d--;
}
} else {
s1[++t] = s[i];
}
}
n = t;
for (int i = 1; i <= n; i++)
s[i] = s1[i];
vector<int> delpos;
for (int i = 0; i < d; i++) {
delpos.push_back(pos[2 * i].first);
delpos.push_back(pos[2 * i + 1].second);
}
t = 0;
reverse(delpos.begin(), delpos.end());
for (int i = 1; i <= n; i++) {
if (!delpos.empty() && delpos.back() == i) {
delpos.pop_back();
} else {
s[++t] = s[i];
}
}
t = n;
assert(delpos.empty());
} int main() {
scanf("%s", s + 1);
n = strlen(s + 1);
shrink();
trans();
make_valid();
make_equal();
for (int i = 1; i <= n; i++)
s[i] = qg[s[i] - 'A'] + 'A';
s[n + 1] = 0;
puts(s + 1);
return 0;
}
Problem F Square Constraints
考虑每个位置有一个上界 $R_i$ 和一个下界 $L_i$。对于后 $N$ 个位置下界为 0。
考虑只有上界,我们把上界从小到大排序,那么答案等于 $\prod_{i = 0}^{2N - 1} (R_i - i)$。
考虑对一些下界进行容斥。不难注意到下界非 0 的位置,它的上界是排在后 $N$ 个位置的上界之后。
所以只用枚举一下有多少个数被硬点不合法就可以 dp 了。
Code
#include <bits/stdc++.h>
using namespace std;
typedef bool boolean; #define pii pair<int, int>
#define ll long long const int N = 505; int n, n2, Mod;
pii a[N];
int cnt[N];
int dp[2][N]; int main() {
scanf("%d%d", &n, &Mod);
n2 = n << 1;
int p1 = 0, p2 = 0, N4 = 4 * n * n, N2 = n * n;
for (int i = n2 - 1; ~i; i--) {
while (p1 < n2 && p1 * p1 <= N4 - i * i)
p1++;
while (p2 * p2 < N2 - i * i)
p2++;
if (p2) {
a[i] = pii(p2, p1);
} else {
a[i] = pii(p1, 0);
}
}
sort(a, a + n2);
cnt[0] = 0;
for (int i = 0; i < n2; i++)
cnt[i + 1] = cnt[i] + !a[i].second;
ll ans = 0;
for (int i = 0; i <= n; i++) {
int cur = 0;
memset(dp[0], 0, sizeof(dp[0]));
dp[0][0] = 1;
for (int j = 0; j < n2; j++) {
memset(dp[cur ^= 1], 0, sizeof(dp[0]));
for (int k = 0; k <= i; k++) {
if (!a[j].second) {
dp[cur][k] = (dp[cur][k] + 1ll * (a[j].first - k - cnt[j]) * dp[cur ^ 1][k]) % Mod;
} else {
dp[cur][k + 1] = (dp[cur][k + 1] - 1ll * (a[j].first - k - cnt[j]) * dp[cur ^ 1][k]) % Mod;
dp[cur][k] = (dp[cur][k] + 1ll * (a[j].second - (n + i + j - cnt[j] - k)) * dp[cur ^ 1][k]) % Mod;
}
}
}
ans += dp[cur][i];
}
ans %= Mod;
(ans < 0) && (ans += Mod);
printf("%d\n", (int) ans);
return 0;
}
AtCoder Grand Contest 036 简要题解的更多相关文章
- AtCoder Grand Contest 031 简要题解
AtCoder Grand Contest 031 Atcoder A - Colorful Subsequence description 求\(s\)中本质不同子序列的个数模\(10^9+7\). ...
- AtCoder Grand Contest 039 简要题解
从这里开始 比赛目录 Problem A Connection and Disconnection 简单讨论即可. Code #include <bits/stdc++.h> using ...
- AtCoder Grand Contest 040 简要题解
从这里开始 比赛目录 A < B < E < D < C = F,心情简单.jpg. Problem A >< 把峰谷都设成 0. Code #include &l ...
- AtCoder Grand Contest 035 简要题解
从这里开始 题目目录 Problem A XOR Circle 你发现,权值的循环节为 $a_0, a_1, a_0\oplus a_1$,然后暴力即可. Code #include <bits ...
- AtCoder Grand Contest 037 简要题解
从这里开始 题目目录 Problem A Dividing a String 猜想每段长度不超过2.然后dp即可. 考虑最后一个长度大于等于3的一段,如果划成$1 + 2$会和后面相同,那么划成$2 ...
- AtCoder Grand Contest 038 简要题解
从这里开始 比赛目录 Problem A 01 Matrix Code #include <bits/stdc++.h> using namespace std; typedef bool ...
- Atcoder Grand Contest 036 D - Negative Cycle
Atcoder Grand Contest 036 D - Negative Cycle 解题思路 在某些情况下,给一张图加或删一些边要使图合法的题目要考虑到最短路的差分约束系统.这一题看似和最短路没 ...
- AtCoder Grand Contest 036题解
传送门 爆炸的比较厉害--果然还是菜啊-- \(A\) 我们强制一个点为\((0,0)\),那么设剩下两个点分别为\((a,b),(c,d)\),根据叉积可以计算出面积为\(ad-bc=S\),那么令 ...
- AtCoder Grand Contest 021完整题解
提示:如果公式挂了请多刷新几次,MathJex的公式渲染速度并不是那么理想. 总的来说,还是自己太弱了啊.只做了T1,还WA了两发.今天还有一场CodeForces,晚上0点qwq... 题解还是要好 ...
随机推荐
- Dockerfile 中的 CMD 与 ENTRYPOINT(转)
add by zhj: CMD和ENTRYPOINT的差异很小,可以认为完全可以相互代替.两者都支持shell模式和exec模式, shell模式:跟你在shell下执行命令的格式一样,简单方便,但 ...
- centos crontab定时任务用法
一.安装crond服务 yum -y update yum -y install cronie yum-cron 二.crontab任务语法 crontab任务配置基本格式: * * * * * co ...
- yield return,yield break
转自, http://www.cnblogs.com/kingcat/archive/2012/07/11/2585943.html yield return 表示在迭代中下一个迭代时返回的数据,除此 ...
- TCP SYN flood洪水攻击原理和防御破解
简介 TCP协议要经过三次握手才能建立连接: 于是出现了对于握手过程进行的攻击.攻击者发送大量的SYN包,服务器回应(SYN+ACK)包,但是攻击者不回应ACK包,这样的话,服务器不知道(SYN+AC ...
- SQL和T-SQL之间的区别
对于SQL,在我的上一篇博客中<何谓SQL Server数据库?与Access数据库 有什么区别>里面,已经着重说明了SQL作为访问和处理数据库的标准的计算机语言,所以这里就不做过多强调. ...
- ORACLE 求和(多列)
SELECT SUM(列名),SUM(列名),SUM(列名),SUM(列名) FROM 表名
- java--标准输入输出流
//读取键盘录入的数据写到a.txt //方式一 private static void method() throws IOException { //创建输入流对象 InputStream is ...
- Java中关于数据类型的一些问题
Java中关于数据类型的一些问题 总结一下最近笔试遇到的一些关于Java中数据类型的一些问题. 虽然比较基础,但在实际做题却很容易出错的点,而且往往这些题出错了会给面试官很不好的感觉:你的基础不好. ...
- flink KMeans算法实现
更正:之前发的有两个错误. 1.K均值聚类算法 百度解释:k均值聚类算法(k-means clustering algorithm)是一种迭代求解的聚类分析算法,其步骤是随机选取K个对象作为初始的聚类 ...
- Windows出现“引用账户被锁定,且暂时无法登录”解决方法
1. 问题描述如下: i. 远程桌面登录时,出现提示必须修改密码才能登录,是因为密码过期所导致的,如下图: ii. 当我们登录Windows控制台(基于OpenStack平台的虚拟机)进行修改密码时, ...