zoukankan      html  css  js  c++  java
  • 0927 DP 小测 #1

    U130826 括号匹配2

    利用前缀和的思想即可,这道题没什么营养就不讲了。

    另外本题有另一种做法,即利用栈括号匹配的性质。考虑一般的括号序列 dp:设 (f_i) 表示以 (i) 结尾的括号匹配串数量, 那么 (f_i = f_{pre_{i}-1} + 1). 也就是硬塞一个。那么只需要根据这段区间内有几个 * 号转移即可。复杂度也是 (O(n)) 的。

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N = 1e6 + 5, CONST = 1e6;
    int n;
    char s[N];
    int pre[N], sum[N], pool[N];
    int op(char x) {
    	if (x == '(') return 1;
    	else return -1;
    }
    void solve() {
    	scanf ("%s", s + 1);
    	n = strlen(s + 1);
    	ll ans = 0;
    	for (int i = 1; i <= n; ++i) if (s[i] == '*') {
    		int l = i - 1, r = i + 1;
    		while (l >= 1 && s[l] != '*') --l;
    		while (r <= n && s[r] != '*') ++r;
    		pre[i] = sum[i] = 0;
    		for (int j = i + 1; j < r; ++j) {
    			sum[j] = sum[j - 1] + op(s[j]);
    			pre[j] = min(pre[j - 1], sum[j]);
    			if (pre[j] >= sum[j]) pool[sum[j] + CONST]++;
    		} 
    		for (int j = i - 1; j > l; --j) {
    			pre[j] = min(pre[j + 1] + op(s[j]), op(s[j]));
    			sum[j] = sum[j + 1] + op(s[j]);
    			if (pre[j] < 0) continue;
    			ans += pool[-sum[j] + CONST];
    		}
    		for (int j = i + 1; j < r; ++j) pool[sum[j] + CONST] = 0;
    	}
    	cout << ans << '
    ';
    } 
    int main() {
    	int t;
    	cin >> t;
    	while (t--) solve();
    	return 0;
    }
    

    U131044 关押罪犯

    首先将所有数排序,那么接下来就是一个 trivial 的 dp: 设 (black_{i, x}, white_{i, x}) 表示 (i) 这个人进入了黑队/白队,上一个与我颜色不同的人数值是 (x) 的方案数,线段树优化 (dp) 转移即可。

    不过发现这个优化转移是一个区间清零,前缀求和的操作,而且之前清零的位置并不会动,那么只需要顺带记录一下之前清零清到了哪里,就可以直接减掉来维护了。复杂度 (O(n log n))(O(n)).

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    typedef vector<int> vi;
    
    const int N = 500505, P = 1e9 + 7;
    int add(int a, int b) {
    	return (a += b) >= P ? a - P : a;
    } int sub(int a, int b) {
    	return (a -= b) < 0 ? a + P : a;
    }
    int read(){
    	int x=0;
    	char ch=getchar();
    	while(ch<'0'||ch>'9')ch=getchar();
    	while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
    	return x;
    }
    int n, Ga, Gb, x[N], y[N], cnt;
    struct BIT {
    	int sum[N];
    	void ADD(int p, int val) {
    		for (; p <= cnt; p += p & -p) sum[p] = add(sum[p], val);
    	}
    	int qry(int p) {
    		int ans = 0;
    		for (; p; p -= p & -p) ans = add(ans, sum[p]);
    		return ans;
    	}
    }I, II;
    int getpos(int t) {
    	return upper_bound(y + 1, y + cnt + 1, t) - (y + 1);
    } 
    int real(int i) {
    	return y[x[i]];
    }
    int main() {
    	cin >> n >> Ga >> Gb;
    	for (int i = 1; i <= n; ++i) x[i]=read(),y[++cnt] = x[i];
    	sort (x + 1, x + n + 1);
    	y[++cnt] = -1e9;
    	sort (y + 1, y + cnt + 1);
    	cnt = unique(y + 1, y + cnt + 1) - (y + 1);
    	for (int i = 1; i <= n; ++i) x[i] = lower_bound(y + 1, y + cnt + 1, x[i]) - y;
    	I.ADD(1, 1);
    	II.ADD(1, 1);
    	int posI = 0, posII = 0;
    	for (int i = 1; i < n; ++i) {
    		int L = getpos(real(i + 1) - Ga), LL = getpos(real(i + 1) - Gb);
    		int for2 = sub(I.qry(L), posI), for1 = sub(II.qry(LL), posII);
    		if (real(i + 1) - real(i) < Gb) posI = I.qry(x[i] - 1);
    		if (real(i + 1) - real(i) < Ga) posII = II.qry(x[i] - 1);
    		II.ADD(x[i], for2);
    		I.ADD(x[i], for1);
    	}
    //	cout << posI << " " << posII << '
    ';
    	cout << sub(add(I.qry(cnt), II.qry(cnt)), add(posI, posII)) << '
    ';
    	return 0;
    }
    

    U131060 最后的球

    这题是代表元计数法的一个 Bonus.

    首先需要注意到对于任意一个后缀,颜色数肯定是小于黑球数量的。那么记 (dp_{i, j}) 为当前放了 (i) 个黑球,(j) 个颜色的方案数。

    决策下一个位置放黑球还是放颜色,如果放颜色的话相当于就是后缀进行一波插板。

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    typedef vector<int> vi;
    
    const int N = 2000 * 2000 + 100, P = 1e9 + 7;
    int n, k;
    int f[2005][2005];
    int fac[N], ifac[N];
    int qpow(int a, int b) {
    	int res(1);
    	for (; b; b >>= 1) {
    		if (b & 1) res = (ll)res * a % P;
    		a = (ll)a * a % P;
    	}
    	return res;
    }
    int C(int a, int b) {
    	if (a < b) return 0;
    //	cout << a << " " << b << ": " << (ll)fac[a] * ifac[a - b] % P * ifac[b] % P << '
    ';
    	return (ll)fac[a] * ifac[a - b] % P * ifac[b] % P;
    }
    int main() {
    	fac[0] = 1;
    	for (int i = 1; i < N; ++i) fac[i] = (ll)fac[i - 1] * i % P;
    	ifac[N - 1] = qpow(fac[N - 1], P - 2);
    	for (int i = N - 2; i >= 0; --i) ifac[i] = (ll)ifac[i + 1] * (i + 1) % P;
    	cin >> n >> k;
    	if (k == 1) return cout << 1, 0;
    	f[0][0] = 1;
    	for (int SUM = 0; SUM <= 2 * n; ++SUM) {
    		for (int i = 0; i <= min(n, SUM); ++i) {
    			int j = SUM - i;
    			if (j > n) continue;
    			// case 1
    			if (i + 1 >= j) (f[i + 1][j] += f[i][j]) %= P;
    			// case 2
    			if (i >= j + 1) (f[i][j + 1] += (ll)f[i][j] * (n - j) % P * C(n * k - i - j * (k - 1) - 1, k - 2) % P) %= P;
    		}
    	} 
    	cout << f[n][n] << '
    ';
    	return 0;
    }
    
  • 相关阅读:
    JAVA 遍历UFT-8所有中文
    用友U8 | 【固定资产】固定资产常见问题
    用友U8 | 【出纳管理】当前凭证不是出纳管理凭证,不能修改
    用友U8 | 【出纳管理】选中的日记账包含了已制单但没有回写凭证号的日记账,无法制单!
    用友U8 | 【生产订单】工作日历未设定或超出工作日历范围
    用友U8 | 【出纳管理】当前选择的日记账已经制单,上次制单包含其他账户的日记账,请在日记账查询中修改
    用友U8 | 【U8年结】开账结转如何操作?
    用友U8 | 【出纳管理】收支操作生单提示:集合中的143272已经加锁了,不能对集合中的数据进行加锁
    用友U8 | 【出纳管理】银行记账提示:未设置对象变量或with block 变量(91)
    MySQL经典45题(二)
  • 原文地址:https://www.cnblogs.com/LiM-817/p/13815698.html
Copyright © 2011-2022 走看看