zoukankan      html  css  js  c++  java
  • 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
    ", 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
    ", 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
    ", 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
    ", 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
    ", (int) ans);
    	return 0;
    }

  • 相关阅读:
    残缺的字符串
    [BZOJ3513: [MUTC2013]idiots]
    FFT感性瞎扯
    Quartz框架简介
    异常状态码总结
    【SSM】拦截器的原理、实现
    FastDFS实现文件上传下载实战
    分布式文件系统FastDFS设计原理(转)
    FastDFS简介
    【设计模式】观察者模式
  • 原文地址:https://www.cnblogs.com/yyf0309/p/agc036.html
Copyright © 2011-2022 走看看