@RabbitHu
2017-09-10T16:28:46.000000Z
字数 3543
阅读 1578
作业
给你一个长度为 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 0x3f3f3f3f
using 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;
}