zoukankan      html  css  js  c++  java
  • AtCoder Grand Contest 040 简要题解

    从这里开始

      A < B < E < D < C = F,心情简单.jpg。

    Problem A ><

      把峰谷都设成 0。

    Code

    #include <bits/stdc++.h>
    using namespace std;
    typedef bool boolean;
    
    const int N = 5e5 + 5;
    
    int n;
    char s[N];
    int L[N], R[N];
    
    int main() {
    	scanf("%s", s + 1);
    	n = strlen(s + 1) + 1;
    	for (int i = 1; i <= n; i++)
    		L[i] = (s[i - 1] == '<') * (L[i - 1] + 1);
    	for (int i = n; i; i--)
    		R[i] = (s[i] == '>') * (R[i + 1] + 1);
    	long long ans = 0;
    	for (int i = 1; i <= n; i++) {
    		ans += max(L[i], R[i]);
    //		cerr << max(L[i], R[i]) << ' ';
    	}
    	printf("%lld
    ", ans);
    	return 0;
    }

    Problem B Two Contests

      考虑如果两个区间有包含,要么它们放在一起,要么长的那一个单独在一场考试。

    Code

    #include <bits/stdc++.h>
    using namespace std;
    typedef bool boolean;
    
    const int N = 1e5 + 5;
    
    const int inf = (signed) (~0u >> 2);
    
    typedef class Segment {
    	public:
    		int l, r;
    
    		Segment() {	}
    		Segment(int l, int r) : l(l), r(r) {	}
    
    		Segment operator & (Segment b) const {
    			return Segment(max(l, b.l), min(r, b.r));
    		}
    		int length() {
    			return max(r - l + 1, 0);
    		}
    		boolean operator < (Segment b) const {
    			return r < b.r;
    		}
    } Segment;
    
    int n;
    Segment s[N], sl[N], sr[N];
    
    int main() {
    	scanf("%d", &n);
    	for (int i = 1; i <= n; i++) {
    		scanf("%d%d", &s[i].l, &s[i].r);
    	}
    	sort(s + 1, s + n + 1);
    	int ans = 0;
    	sl[0] = Segment(0, inf);
    	for (int i = 1; i <= n; i++)
    		sl[i] = sl[i - 1] & s[i];
    	sr[n + 1] = Segment(0, inf);
    	for (int i = n; i; i--)
    		sr[i] = sr[i + 1] & s[i];
    	for (int i = 1; i < n; i++)
    		ans = max(ans, sl[i].length() + sr[i + 1].length());
    	for (int i = 1; i <= n; i++)
    		ans = max(ans, s[i].length() + (sl[i - 1] & sr[i + 1]).length());
    	printf("%d
    ", ans);
    	return 0;
    }

    Problem C Neither AB nor BA

      考虑把奇数位置上的 A 变成 B,B 变成 A,那么现在变成只有 AA 和 BB 不能消除。

      考虑能够消除所有字符的必要条件是 A 或者 B 的数量不超过 $n / 2$。不难发现这个条件也是充分的。

      如果不存在 C,那么直接消除 AB 即可。

      考虑如果 C 只与 A 或者 B 相邻,那么先把 AB 相互消除,最后和 C 消除,如果还剩一些 C,那么把 C 两两消除。因为 A,B 的数量都不超过 $n / 2$,所以它们相互消除后剩下的字符数量不会超过 C 的数量。

      否则如果 A,B 的数量一样多,那么至少有 2 个 C,消除 1 个 AC 和 1 个 BC,否则把 C 和较多一个字符消除。显然这样仍然满足条件,可以递归到 $n$ 更小的问题。

      然后直接计数就行了。

    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 = 1e7 + 7;
    
    int n;
    Zi fac[N], _fac[N], pw2[N];
    
    Zi comb(int n, int m) {
    	return (n < m) ? (0) : (fac[n] * _fac[m] * _fac[n - m]);
    }
    
    int main() {
    	scanf("%d", &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;
    	pw2[0] = 1;
    	for (int i = 1; i <= n + 1; i++)
    		pw2[i] = pw2[i - 1] + pw2[i - 1];
    	Zi ans = qpow(3, n);
    	for (int i = (n >> 1) + 1; i <= n; i++)
    		ans -= comb(n, i) * pw2[n - i + 1];
    	printf("%d
    ", ans.v);
    	return 0;
    }

    Problem D Balance Beam

      一道贪心题就有 100 种假贪心。

      假设我们已经定好了顺序,设 $B_i - A_i$ 的前缀 max 为 $mx$,显然答案可以这样计算

    for (int i = 1; i <= n; i++) {
      if (sum >= B[i]) {
        sum -= B[i];
        ans += 1;
      } else {
        ans += 1.0 * sum / B[i];
        break;
      }
    }
    

      考虑枚举进入 else 语句的 $i$。计算贡献可以分为 3 部分:$[1, i), (i, pmx], (pmx, n]$,其中 $pmx$ 表示前缀 max 的位置。先考虑把所有 $A_i < B_i$ 的平衡木加入 $i$ 之后 $pmx$ 之前,然后考虑把一些移动到 i 之前,不难发现移动的代价为 $max{A_i, B_i}$。排序后二分一下最多能选到哪即可。

    Code

    #include <bits/stdc++.h>
    using namespace std;
    typedef bool boolean;
    
    #define ll long long
    #define ld long double
    
    template <typename T>
    boolean vmax(T& a, T b) {
    	return (a < b) ? (a = b, true) : (false);
    }
    
    const int N = 1e5 + 5;
    const ll llf = 1e17;
    const int inf = (signed) (~0u >> 2);
    
    ll gcd(ll a, ll b) {
    	return (b) ? (gcd(b, a % b)) : (a);
    }
    
    typedef class Fraction {
    	public:
    		ll a, b;
    
    		Fraction() : a(0), b(1) {	}
    		Fraction(ll a, ll b) : a(a), b(b) {	}
    
    		boolean operator < (Fraction B) const {
    			return ((ld) a) * B.b - ((ld) b) * B.a < 0;
    		}
    		boolean operator > (Fraction B) const {
    			return ((ld) a) * B.b - ((ld) b) * B.a > 0;
    		}
    } Fraction;
    
    #define pii pair<int, int>
    
    typedef class Item {
    	public:
    		int a, b, id;
    
    		Item() {	}
    		Item(int a, int b, int id) : a(a), b(b), id(id) {	}
    
    		int maxval() const {
    			return max(a, b);
    		}
    		boolean operator < (Item i) const {
    			return maxval() < i.maxval();
    		}
    } Item;
    
    int n;
    vector<Item> E;
    ll _presum[N], *presum;
    
    int main() {
    	scanf("%d", &n);
    	ll _sum = 0;
    	for (int i = 1, a, b; i <= n; i++) {
    		scanf("%d%d", &a, &b);
    		E.emplace_back(a, b, i);
    		_sum += max(b - a, 0);
    	}
    	sort(E.begin(), E.end());
    	Fraction ans (0, 1), I (1, 1);
    	presum = _presum + 1;
    	presum[-1] = 0;
    	for (int i = 0; i < n; i++)
    		presum[i] = presum[i - 1] + E[i].maxval();
    	for (int j = 0; j < n; j++) {
    		ll sum = _sum, ts;
    		Item& e = E[j];
    		if (e.a > e.b)
    			sum += e.b - e.a;
    		int l = 0, r = n - 1, mid;
    #define calc(p) (presum[p] - (p >= j) * e.maxval())
    		while (l <= r) {
    			mid = (l + r) >> 1;
    			ts = calc(mid);
    			if (ts <= sum) {
    				l = mid + 1;
    			} else {
    				r = mid - 1;
    			}
    		}
    		Fraction tmp (sum - calc(l - 1), e.b);
    		tmp = min(tmp, I);
    		tmp.a += tmp.b * (l - (l > j));
    		ans = max(ans, tmp);
    	}
    	ll g = gcd(ans.a, ans.b *= n);
    	ans.a /= g;
    	ans.b /= g;
    	printf("%lld %lld
    ", ans.a, ans.b);
    	return 0;
    }

    Problem E Prefix Suffix Addition

      显然可以让前缀加非 0 的位置不相交,后缀加非 0 的位置不相交,并且不会更劣。

      问题等价于把原序列拆成两个序列 ${x_i}, {y_i}$,满足

    • $x_0 = y_0 = x_{n + 1} = y_{n + 1} = 0$

    • $x_i geqslant 0, y_i geqslant 0, x_i + y_i = a_i$。

      要求最小化 $sum [x_i > x_{i + 1}] + [y_i < y_{i + 1}]$。

      设 $f_{i, j}$ 表示考虑到序列的第 $i$ 位,$x_i$ 的值为 $j$ 的最小代价。

      不难发现两个性质:

    • $f_{i, j}$ 的极差小于等于 2
    • $f_{i, j}$ 单调不增

      然后记录一下分界点,转移二分一下就行了。

    Code

    #include <bits/stdc++.h>
    using namespace std;
    typedef bool boolean;
    
    const int N = 2e5 + 5;
    
    typedef class dp_t {
    	public:
    		int v0, p1, p2, a;
    
    		dp_t() {	}
    		dp_t(int v0, int p1, int p2, int a) : v0(v0), p1(p1), p2(p2), a(a) {	}
    
    		int eval(int x, int na) {
    #define calc(xls, fold) ((fold) + (xls > x) + (a - xls < na - x))
    			int rt = calc(0, v0);
    			if (p1 <= a)
    				rt = min(rt, calc(p1, v0 - 1));
    			if (p2 <= a)
    				rt = min(rt, calc(p2, v0 - 2));
    			return rt;
    		}
    		dp_t trans(int na) {
    			dp_t rt (0, 0, 0, na);
    			rt.v0 = eval(0, na);
    			int l = 0, r = na, mid;
    			while (l <= r) {
    				mid = (l + r) >> 1;
    				if (eval(mid, na) == rt.v0) {
    					l = mid + 1;
    				} else {
    					r = mid - 1;
    				}
    			}
    			rt.p1 = l;
    			r = na;
    			while (l <= r) {
    				mid = (l + r) >> 1;
    				if (eval(mid, na) == rt.v0 - 1) {
    					l = mid + 1;
    				} else {
    					r = mid - 1;
    				}
    			}
    			rt.p2 = l;
    			return rt;
    		}
    } dp_t;
    
    int n;
    int a[N];
    
    int main() {
    	scanf("%d", &n);
    	for (int i = 1; i <= n; i++) {
    		scanf("%d", a + i);
    	}
    	dp_t f (0, 1, 1, 0);
    	for (int i = 1; i <= n; i++)
    		f = f.trans(a[i]);
    	f = f.trans(0);
    	printf("%d
    ", f.v0);
    	return 0;
    }

    Problem F Two Pieces

      考虑用 $(x, d)$ 来表示一个状态,其中较大数在 $x$,较小数和较大数的差为 $d$。那么可以通过一些限制转化成求操作序列个数,有如下三种操作:

    • 将 $x, d$ 同时加上 1
    • 将 $d$ 减少 1,这个时候必须满足 $d geqslant 2$
    • 将 $d$ 设为 0.

      你发现前面两个操作"很 Catalan",考虑确定前两个操作序列,然后插入第三种操作。

      不难发现第 1 种操作会恰好执行 $B$ 次,考虑枚举第 $2$ 种操作的操作次数 $k$。然后能用折线法计算方案数。

      如果 $N = B + k$,这种情况很 trivial,可以直接做。否则考虑 $B + k < N$ 的情况。

      考虑如何插入第 3 种操作,注意到它插入后必须同时满足两个条件:

    • 纵坐标到达 $B - A$。
    • 不会使第二种操作不合法。

      注意到它的影响相当于把一段后缀的 $d$ 减去其中第一个 $d$ 的值。所以为了满足第二个条件它必须是严格的后缀最小值,为了满足第一个条件它需要操作最后一个 $d = A - k$。

      满足这之后,可以把操作 3 插入任意一个满足 $d leqslant A - k$ 的严格后缀最小值处。这个直接插板法算方案即可。

    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 = 2e7 + 7;
    
    int n, A, B;
    Zi fac[N], _fac[N];
    
    void init(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]);
    }
    Zi calc(int x, int y) {
    	return comb(x + y, x) - comb(x + y, x + 1);
    }
    
    int main() {
    	scanf("%d%d%d", &n, &A, &B);
    	if (!B) {
    		puts("1");
    		return 0;
    	} 
    	init(n << 1);
    	Zi ans = 0;
    	for (int k = 0; k <= n - B && k <= A; k++) {
    		if (B + k == n) {
    			ans += (A == k) * calc(B - 1, A);
    		} else {
    			ans += calc(B - 1, k) * comb(n - B - k - 1 + A - k, A - k);
    		}
    	}
    	printf("%d
    ", ans.v);
    	return 0;
    }

  • 相关阅读:
    SSH移植
    ARM PPC 交叉编译环境搭建
    CEOI2020 作战记录&题解
    [NOI2019]序列 题解
    Atcoder Dwango Programming Contest 6th 题解
    CF516D Drazil and Morning Exercise 题解
    CF568E Longest Increasing Subsequence 题解
    [IOI2013]robots 题解
    SPOJ22549 DIVFACT4
    CF590E Birthday 题解
  • 原文地址:https://www.cnblogs.com/yyf0309/p/agc040.html
Copyright © 2011-2022 走看看