@RabbitHu
2017-09-10T08:28:46.000000Z
字数 3543
阅读 1935
作业
给你一个长度为 n 的括弧序列和 m 组询问,每次询问区间[l,r]中最长合法括号子序列 合法括号序列定义如下:
1. 空序列是合法括号序列。
2. 若 S 是合法括号序列则"(S)"是合法括号序列。
3. 若 S1、S2 是合法括号序列,则"S1S2"是合法括号序列 括弧序列定义如下: 由“(”和“)”组成的序列。
离线,将询问按右端点排序,从左往右处理括号序列,左括号则入栈,右括号则匹配一个左括号,被匹配的左括号在一个数组中对应的位置 + 1。
如果当前处理到的位置是某个询问区间的右端点,则这个询问的答案是询问区间的区间和。
#include <cstdio>#include <cmath>#include <algorithm>#include <iostream>#include <cstring>using namespace std;int read(){char c;bool op = 0;while((c = getchar()) < '0' || c > '9')if(c == '-') op = 1;int ret = c - '0';while((c = getchar()) >= '0' && c <= '9')ret = ret * 10 + c - '0';return op ? -ret : ret;}void write(int x){if(x < 0) putchar('-'), x = -x;if(x >= 10) write(x / 10);putchar('0' + x % 10);}const int N = 400005, M = 200005;int n, m;char s[N];int stk[N], top, tree[N], ans[M];struct query {int id, l, r;bool operator < (const query &b) const {return r < b.r;}} q[M];void add(int p){while(p <= n) tree[p] ++, p += p & -p;}int ask(int p){int ret = 0;while(p) ret += tree[p], p -= p & -p;return ret;}int main(){n = read(), m = read();scanf("%s", s + 1);for(int i = 1; i <= m; i++)q[i].id = i, q[i].l = read(), q[i].r = read();sort(q + 1, q + m + 1);for(int i = 1, pos = 1; i <= n; i++){if(s[i] == '(') stk[++top] = i;else if(top) add(stk[top--]);while(i == q[pos].r)ans[q[pos].id] = ask(i) - ask(q[pos].l - 1), pos++;}for(int i = 1; i <= m; i++)write(ans[i] << 1), putchar('\n');return 0;}
有n个英雄等待复活,英雄复活需要在复活神坛支付一定金额才能复活。复活神坛一共
有 m 种方法复活英雄,每种方法需要支付 ai 金复活,由于复活需要支付大量法力值,所以 一种复活方法只能使用一次。
每个英雄都有 bi 非奖励金额和奖励金额 ki。由于奖励金额只能用于复活,所以英雄们 尽可能少用非奖励金额复活。为了守住高地,现在需要你用最快的时间算出最多能让多少个 英雄复活,并在复活英雄人数最多的前提下,保证花费非奖励金额最少。
考试的时候 我根本没看懂这道题的题面!
实际上这道题是贪心,先贪心求出能复活多少人,之后再对于每一个复活方式,复活能复活的人中奖励金额最多的人。
#include <cstdio>#include <cmath>#include <algorithm>#include <iostream>#include <cstring>#include <queue>using namespace std;typedef long long ll;int read(){char c;bool op = 0;while((c = getchar()) < '0' || c > '9')if(c == '-') op = 1;int ret = c - '0';while((c = getchar()) >= '0' && c <= '9')ret = ret * 10 + c - '0';return op ? -ret : ret;}const int N = 200005;int n, m, p[N], ans;ll tot;struct hero {int a, b, s;bool operator < (const hero &obj) const{return b < obj.b;}} h[N];priority_queue <hero> q;bool cmp(const hero &x, const hero &y){return x.s < y.s;}int main(){n = read(), m = read();for(int i = 1; i <= n; i++)h[i].a = read();for(int i = 1; i <= n; i++)h[i].b = read(), h[i].s = h[i].a + h[i].b;for(int i = 1; i <= m; i++)p[i] = read();sort(p + 1, p + m + 1);sort(h + 1, h + n + 1, cmp);int i, j;for(i = n, j = m; i > 0 && j > 0; i--, j--){while(j > 0 && h[i].s < p[j]) j--;if(j == 0) break;}ans = n - i;for(i = n, j = ans; j > 0; j--){while(i && h[i].s >= p[j]) q.push(h[i]), i--;tot += max(0, p[j] - q.top().b);q.pop();}printf("%d %lld\n", ans, tot);return 0;}
小 Q 要在一个长度为 s 的火球符文串中学习火球术,他希望学习一个等级高的火球术,
定义火球术的等级 fire(s,p)表示火球符文串 s 中最多有多少个互不重叠的子串 p(p 是强 化符文)。
小 Q 发现如果直接学习火球符文串的话火球术等级会十分低,所以小 Q 决定在火球符文 串的任意位置删除任意个符文,使得新的火球符文串能学习到的火球术等级尽量高。 但是小 Q 希望训练你的奥术能力,于是你需要算出在删除 i 个(0<=i<=|s|)符文
后,fire(ss,p)的答案【ss 表示删除 i 个符文后的火球符文串】。
考试的时候这道题我是这样做的:
写个暴力;写个对拍脚本;写个“正解”;while(对拍出错)稍作修改;完成;
后来对拍打印了一整页的AC后我就当这道题写完了……
思路大概是这样的:f[i][j]表示到i为止最多删除j个字符能得到的最大连续子串数,g[i]表示i前面找到的第一个能做成连续子串的左侧字符位置 - 1,del[i]为形成g[i]子串需要删掉多少字符。
然后……转移一下?
#include <cstdio>#include <cmath>#include <algorithm>#include <iostream>#include <cstring>#define INF 0x3f3f3f3fusing namespace std;const int N = 4005, S = 23, M = 4003;int n, m;char s[N], p[N];int f[M][M], g[M], del[M], ans2[M];void solve2(){for(int i = n; i; i--){ // O(n ^ 2)int x = i, y = m;while(x && y){while(x && s[x] != p[y]) x--;if(s[x] == p[y]) x--, y--;}g[i] = x;if(y) del[i] = INF;else{x++, y++;while(x <= n && y <= m){while(x <= n && s[x] != p[y]) x++;if(s[x] == p[y]) x++, y++;}del[i] = x - g[i] - 1 - m;}}for(int i = 1; i <= n; i++){for(int j = 0; j <= n; j++){f[i][j] = f[i - 1][j];if(j >= del[i]) f[i][j] = f[g[i]][j - del[i]] + 1;ans2[j] = max(f[i][j], ans2[j]);}}int maxn = ans2[n], lim = n - maxn * m;for(int i = lim; i <= n; i += m)for(int j = 1; j <= m; j++)ans2[i + j] = ans2[i] - 1;for(int i = 0; i <= n; i++)printf("%d ", ans2[i]);printf("\n");}int main(){scanf("%s%s", s + 1, p + 1);n = strlen(s + 1), m = strlen(p + 1);solve2();return 0;}