省选测试2
T1
题目链接
我们可以对每个物品的(a)从小到大排序, 对每个询问的(m)也从小到大排序(离线下来). 这样我们枚举每个小于等于(m)的物品就好了.
假设当前枚举到了第(j)个物品, (f[k])表示前(j)个物品从中选出若干个的(c)恰好凑成(k)的那些物品里, 最小的(b)的最大值.
转移的话就是背包, (f[k] = max(f[k], min(f[k-a[j].c],a[j].b))).注意到倒叙枚举.
最后判断一个询问的(k)的(f)值是否大于(m+s)就好了.
#include <bits/stdc++.h>
using namespace std;
inline long long read() {
long long s = 0, f = 1; char ch;
while(!isdigit(ch = getchar())) (ch == '-') && (f = -f);
for(s = ch ^ 48;isdigit(ch = getchar()); s = (s << 1) + (s << 3) + (ch ^ 48));
return s * f;
}
const int N = 1005, M = 1e5 + 5, K = 1e6 + 5;
int n, q_;
int f[M], ans[K];
struct object {
int a, b, c;
friend int operator < (const object &x, const object &y) {
return x.a < y.a;
}
} a[N];
struct ques {
int m, k, s, id;
friend int operator < (const ques &a, const ques &b) {
return a.m < b.m;
}
} q[K];
int main() {
freopen("a.in","r",stdin); freopen("a.out","w",stdout);
n = read();
for(int i = 1;i <= n; i++)
a[i].c = read(), a[i].a = read(), a[i].b = read();
sort(a + 1, a + n + 1);
q_ = read();
for(int i = 1;i <= q_; i++)
q[i].m = read(), q[i].k = read(), q[i].s = read(), q[i].id = i;
sort(q + 1, q + q_ + 1);
int j = 1;
f[0] = 1e9 + 10;
for(int i = 1;i <= q_; i++) {
for(; a[j].a <= q[i].m && j <= n; j++)
for(int k = M - 5;k >= a[j].c; k--)
f[k] = max(f[k], min(f[k - a[j].c], a[j].b));
if(f[q[i].k] > q[i].m + q[i].s) ans[q[i].id] = 1;
}
for(int i = 1;i <= q_; i++)
printf("%s
", ans[i] ? "TAK" : "NIE");
fclose(stdin); fclose(stdout);
return 0;
}
/*
5
6 2 7
5 4 9
1 2 4
2 5 8
1 3 9
5
2 7 1
2 7 2
3 2 0
5 7 2
4 1 5
*/
T2
题目链接
首先(O(n^2))的dp应该很好想 : (f[i] = min(f[j], max{h[j+1]...h[i]})(sum_i-sum_j<=L))
(sum)代表前缀和, (f[i])代表前(i)个分成合法方式的最小高度.
我们可以考虑用线段树优化一下.
线段树维护的是(f[j]),以及(f[j] + max{h[j+1]...h[i]}), 我们分别用(Max,F)表示.
假设当前要更新(f[i]),那么(h[i])会影响的应该是(lm[i])到(i-1)这一段的(Max).(lm[i])代表的是(i)左边第一个小于等于(h[i])的位置.
然后我们用(h[i])取更新区间([lm[i],i-1])的(F)值, 也就是对于一个节点(o), 把它的(F)值更新为(Max[o]+h[i]).
现在我们就可以更新(f[i])了, 我们用二分法找到最左边的(sum_i-sum_j <=L)的(j)的位置, 然后查询区间([j,i-1])的最小的(F).
最后我们需要把新的(f[i])放入到线段树中取, 也就是单点修改位置(i)处的(Max)和(F)值.
#include <bits/stdc++.h>
#define int long long
#define ls(o) (o << 1)
#define rs(o) (o << 1 | 1)
#define mid ((l + r) >> 1)
using namespace std;
inline long long read() {
long long s = 0, f = 1; char ch;
while(!isdigit(ch = getchar())) (ch == '-') && (f = -f);
for(s = ch ^ 48;isdigit(ch = getchar()); s = (s << 1) + (s << 3) + (ch ^ 48));
return s * f;
}
const int N = 1e5 + 5, inf = 1e9 + 10;
const int Inf = 1e18;
int n, L, top;
int h[N], w[N], lm[N], sta[N], Max[N << 2], tag[N << 2];
long long f[N << 2], sum[N];
void up(int o) {
Max[o] = min(Max[ls(o)], Max[rs(o)]);
f[o] = min(f[ls(o)], f[rs(o)]);
}
void build(int o, int l, int r) {
if(l == r) { f[o] = Inf; Max[o] = inf; return ; }
build(ls(o), l, mid); build(rs(o), mid + 1, r);
up(o);
}
void modify(int o, int k) {
tag[o] = k;
f[o] = Max[o] + k;
}
void down(int o) {
if(tag[o]) modify(ls(o), tag[o]), modify(rs(o), tag[o]), tag[o] = 0;
}
void change_f(int o, int l, int r, int x, int val) {
if(l == r) { f[o] = Max[o] = val; return ; }
down(o);
if(x <= mid) change_f(ls(o), l, mid, x, val);
if(x > mid) change_f(rs(o), mid + 1, r, x, val);
up(o);
}
void change_mx(int o, int l, int r, int x, int y, int val) {
if(x <= l && y >= r) { modify(o, val); return ; }
down(o);
if(x <= mid) change_mx(ls(o), l, mid, x, y, val);
if(y > mid) change_mx(rs(o), mid + 1, r, x, y, val);
up(o);
}
long long query(int o, int l, int r, int x, int y) {
if(x <= l && y >= r) return f[o];
down(o);
long long res = Inf;
if(x <= mid) res = min(res, query(ls(o), l, mid, x, y));
if(y > mid) res = min(res, query(rs(o), mid + 1, r, x, y));
return res;
}
signed main() {
n = read(); L = read();
for(int i = 1;i <= n; i++) h[i] = read(), w[i] = read();
for(int i = 1;i <= n; i++) sum[i] = sum[i - 1] + w[i];
sta[++ top] = 0;
for(int i = 1;i <= n; i++) {
while(top && h[sta[top]] <= h[i]) top --;
lm[i] = sta[top]; sta[++ top] = i;
}
// for(int i = 1;i <= n; i++) cout << lm[i] << " "; cout << "
";
build(1, 0, n); change_f(1, 0, n, 0, 0);
long long res;
for(int i = 1;i <= n; i++) {
change_mx(1, 0, n, lm[i], i - 1, h[i]);
int p = lower_bound(sum, sum + n + 1, sum[i] - L) - sum;
res = query(1, 0, n, p, i - 1);
change_f(1, 0, n, i, res);
}
printf("%lld
", res);
return 0;
}
T3
题目链接
树上主席树 + 启发式合并.
假设没有连边操作. 我们遍历这颗树, 让每个节点在它的父亲节点的基础上插入新节点. 对于一个查询(x, y), 我们可以先求出它们的(lca), 然后在主席树上这样查询区间内有多少个数 : (siz[x] + siz[y] - siz[lca] - siz[fa[lca]]).
那有连边操作怎么办呢? 为了保证复杂度, 我们选择启发式合并, 也就是把小的树合并到大的树上面. 用并查集维护一下每一个联通块的大小, 每次把小的那部分合并上去后就重新遍历一遍, 求出新的倍增数组(为了求(lca)).
#include <bits/stdc++.h>
#define ls(o) t[o].ls
#define rs(o) t[o].rs
#define mid ((l + r) >> 1)
using namespace std;
inline long long read() {
long long s = 0, f = 1; char ch;
while(!isdigit(ch = getchar())) (ch == '-') && (f = -f);
for(s = ch ^ 48;isdigit(ch = getchar()); s = (s << 1) + (s << 3) + (ch ^ 48));
return s * f;
}
const int N = 8e4 + 5, M = 5e7;
int n, m, t_, tot, cnt, cnt_b;
int b[N], rt[N], fa[N], siz[N], val[N], dep[N], head[N], f[N][21];
struct tree { int ls, rs, siz; } t[M];
struct edge { int to, nxt; } e[N << 1];
int find(int x) {
return fa[x] == x ? x : fa[x] = find(fa[x]);
}
void add(int x, int y) {
e[++ cnt].nxt = head[x]; head[x] = cnt; e[cnt].to = y;
}
void build(int &o, int l, int r) {
o = ++ tot;
if(l == r) return ;
build(ls(o), l, mid); build(rs(o), mid + 1, r);
}
int LCA(int x, int y) {
if(dep[x] < dep[y]) swap(x, y);
for(int i = 20;i >= 0; i--) if(dep[x] - dep[y] >= (1 << i)) x = f[x][i];
if(x == y) return x;
for(int i = 20;i >= 0; i--) if(f[x][i] != f[y][i]) x = f[x][i], y = f[y][i];
return f[x][0];
}
void insert(int &o, int p, int l, int r, int x) {
o = ++ tot;
t[o] = t[p]; t[o].siz ++;
if(l == r) return ;
if(x <= mid) insert(ls(o), ls(p), l, mid, x);
if(x > mid) insert(rs(o), rs(p), mid + 1, r, x);
}
int query(int a_, int b_, int c_, int d_, int l, int r, int k) {
if(l == r) return l;
int res = t[ls(a_)].siz + t[ls(b_)].siz - t[ls(c_)].siz - t[ls(d_)].siz;
if(res >= k) return query(ls(a_), ls(b_), ls(c_), ls(d_), l, mid, k);
else return query(rs(a_), rs(b_), rs(c_), rs(d_), mid + 1, r, k - res);
}
void get_tree(int x, int Fa) {
dep[x] = dep[Fa] + 1; f[x][0] = Fa;
insert(rt[x], rt[Fa], 1, cnt_b, val[x]);
for(int i = 1;i <= 20; i++) f[x][i] = f[f[x][i - 1]][i - 1];
for(int i = head[x]; i ; i = e[i].nxt) {
int y = e[i].to; if(y == Fa) continue ;
get_tree(y, x);
}
}
int main() {
freopen("c.in","r",stdin); freopen("c.out","w",stdout);
int testcase = read();
n = read(); m = read(); t_ = read();
for(int i = 1;i <= n; i++) b[i] = val[i] = read();
for(int i = 1;i <= n; i++) fa[i] = i, siz[i] = 1;
sort(b + 1, b + n + 1);
cnt_b = unique(b + 1, b + n + 1) - b - 1;
for(int i = 1;i <= n; i++) val[i] = lower_bound(b + 1, b + cnt_b + 1, val[i]) - b;
for(int i = 1, x, y;i <= m; i++) {
x = read(); y = read();
if(x == y) continue ;
add(x, y); add(y, x);
x = find(x); y = find(y);
if(x == y) continue ;
if(siz[x] > siz[y]) swap(x, y);
fa[x] = y; siz[y] += siz[x];
}
build(rt[0], 1, cnt_b);
for(int i = 1;i <= n; i++) if(!dep[i]) get_tree(i, 0);
int las = 0;
for(int i = 1, x, y, k;i <= t_; i++) {
char ch_; cin >> ch_;
if(ch_ == 'Q') {
x = read() ^ las; y = read() ^ las; k = read() ^ las;
int lca = LCA(x, y);
// cout << x << " " << y << " " << lca << " " << f[lca][0] << "!!!
";
las = b[query(rt[x], rt[y], rt[lca], rt[f[lca][0]], 1, cnt_b, k)];
printf("%d
", las);
}
if(ch_ == 'L') {
x = read() ^ las; y = read() ^ las;
int t1 = x, t2 = y;
add(t1, t2); add(t2, t1);
x = find(x), y = find(y);
if(siz[x] > siz[y]) swap(x, y), swap(t1, t2);
fa[x] = y; siz[y] += siz[x];
get_tree(t1, t2);
}
}
fclose(stdin); fclose(stdout);
return 0;
}